| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- \# 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{<stdint.h>}, \cw{<stdbool.h>} and \c{inline}), but
- you shouldn't use things that are new in C11 (such as \cw{<uchar.h>}
- 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{<inttypes.h>}. 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{<inttypes.h>} 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{<ctype.h>} 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: \c{flags} defines properties that are expected to
- apply equally to \e{all} the sessions run by a single PuTTY process,
- the random number state in \cw{sshrand.c} and the timer list in
- \cw{timing.c} serve all sessions equally, and so on. 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-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!
|