Skip to content
Snippets Groups Projects
  • Simon Tatham's avatar
    2ca0070f
    Move most of ssh.c out into separate source files. · 2ca0070f
    Simon Tatham authored
    I've tried to separate out as many individually coherent changes from
    this work as I could into their own commits, but here's where I run
    out and have to commit the rest of this major refactoring as a
    big-bang change.
    
    Most of ssh.c is now no longer in ssh.c: all five of the main
    coroutines that handle layers of the SSH-1 and SSH-2 protocols now
    each have their own source file to live in, and a lot of the
    supporting functions have moved into the appropriate one of those too.
    
    The new abstraction is a vtable called 'PacketProtocolLayer', which
    has an input and output packet queue. Each layer's main coroutine is
    invoked from the method ssh_ppl_process_queue(), which is usually
    (though not exclusively) triggered automatically when things are
    pushed on the input queue. In SSH-2, the base layer is the transport
    protocol, and it contains a pair of subsidiary queues by which it
    passes some of its packets to the higher SSH-2 layers - first userauth
    and then connection, which are peers at the same level, with the
    former abdicating in favour of the latter at the appropriate moment.
    SSH-1 is simpler: the whole login phase of the protocol (crypto setup
    and authentication) is all in one module, and since SSH-1 has no
    repeat key exchange, that setup layer abdicates in favour of the
    connection phase when it's done.
    
    ssh.c itself is now about a tenth of its old size (which all by itself
    is cause for celebration!). Its main job is to set up all the layers,
    hook them up to each other and to the BPP, and to funnel data back and
    forth between that collection of modules and external things such as
    the network and the terminal. Once it's set up a collection of packet
    protocol layers, it communicates with them partly by calling methods
    of the base layer (and if that's ssh2transport then it will delegate
    some functionality to the corresponding methods of its higher layer),
    and partly by talking directly to the connection layer no matter where
    it is in the stack by means of the separate ConnectionLayer vtable
    which I introduced in commit 8001dd4c, and to which I've now added
    quite a few extra methods replacing services that used to be internal
    function calls within ssh.c.
    
    (One effect of this is that the SSH-1 and SSH-2 channel storage is now
    no longer shared - there are distinct struct types ssh1_channel and
    ssh2_channel. That means a bit more code duplication, but on the plus
    side, a lot fewer confusing conditionals in the middle of half-shared
    functions, and less risk of a piece of SSH-1 escaping into SSH-2 or
    vice versa, which I remember has happened at least once in the past.)
    
    The bulk of this commit introduces the five new source files, their
    common header sshppl.h and some shared supporting routines in
    sshcommon.c, and rewrites nearly all of ssh.c itself. But it also
    includes a couple of other changes that I couldn't separate easily
    enough:
    
    Firstly, there's a new handling for socket EOF, in which ssh.c sets an
    'input_eof' flag in the BPP, and that responds by checking a flag that
    tells it whether to report the EOF as an error or not. (This is the
    main reason for those new BPP_READ / BPP_WAITFOR macros - they can
    check the EOF flag every time the coroutine is resumed.)
    
    Secondly, the error reporting itself is changed around again. I'd
    expected to put some data fields in the public PacketProtocolLayer
    structure that it could set to report errors in the same way as the
    BPPs have been doing, but in the end, I decided propagating all those
    data fields around was a pain and that even the BPPs shouldn't have
    been doing it that way. So I've reverted to a system where everything
    calls back to functions in ssh.c itself to report any connection-
    ending condition. But there's a new family of those functions,
    categorising the possible such conditions by semantics, and each one
    has a different set of detailed effects (e.g. how rudely to close the
    network connection, what exit status should be passed back to the
    whole application, whether to send a disconnect message and/or display
    a GUI error box).
    
    I don't expect this to be immediately perfect: of course, the code has
    been through a big upheaval, new bugs are expected, and I haven't been
    able to do a full job of testing (e.g. I haven't tested every auth or
    kex method). But I've checked that it _basically_ works - both SSH
    protocols, all the different kinds of forwarding channel, more than
    one auth method, Windows and Linux, connection sharing - and I think
    it's now at the point where the easiest way to find further bugs is to
    let it out into the wild and see what users can spot.
    2ca0070f
    History
    Move most of ssh.c out into separate source files.
    Simon Tatham authored
    I've tried to separate out as many individually coherent changes from
    this work as I could into their own commits, but here's where I run
    out and have to commit the rest of this major refactoring as a
    big-bang change.
    
    Most of ssh.c is now no longer in ssh.c: all five of the main
    coroutines that handle layers of the SSH-1 and SSH-2 protocols now
    each have their own source file to live in, and a lot of the
    supporting functions have moved into the appropriate one of those too.
    
    The new abstraction is a vtable called 'PacketProtocolLayer', which
    has an input and output packet queue. Each layer's main coroutine is
    invoked from the method ssh_ppl_process_queue(), which is usually
    (though not exclusively) triggered automatically when things are
    pushed on the input queue. In SSH-2, the base layer is the transport
    protocol, and it contains a pair of subsidiary queues by which it
    passes some of its packets to the higher SSH-2 layers - first userauth
    and then connection, which are peers at the same level, with the
    former abdicating in favour of the latter at the appropriate moment.
    SSH-1 is simpler: the whole login phase of the protocol (crypto setup
    and authentication) is all in one module, and since SSH-1 has no
    repeat key exchange, that setup layer abdicates in favour of the
    connection phase when it's done.
    
    ssh.c itself is now about a tenth of its old size (which all by itself
    is cause for celebration!). Its main job is to set up all the layers,
    hook them up to each other and to the BPP, and to funnel data back and
    forth between that collection of modules and external things such as
    the network and the terminal. Once it's set up a collection of packet
    protocol layers, it communicates with them partly by calling methods
    of the base layer (and if that's ssh2transport then it will delegate
    some functionality to the corresponding methods of its higher layer),
    and partly by talking directly to the connection layer no matter where
    it is in the stack by means of the separate ConnectionLayer vtable
    which I introduced in commit 8001dd4c, and to which I've now added
    quite a few extra methods replacing services that used to be internal
    function calls within ssh.c.
    
    (One effect of this is that the SSH-1 and SSH-2 channel storage is now
    no longer shared - there are distinct struct types ssh1_channel and
    ssh2_channel. That means a bit more code duplication, but on the plus
    side, a lot fewer confusing conditionals in the middle of half-shared
    functions, and less risk of a piece of SSH-1 escaping into SSH-2 or
    vice versa, which I remember has happened at least once in the past.)
    
    The bulk of this commit introduces the five new source files, their
    common header sshppl.h and some shared supporting routines in
    sshcommon.c, and rewrites nearly all of ssh.c itself. But it also
    includes a couple of other changes that I couldn't separate easily
    enough:
    
    Firstly, there's a new handling for socket EOF, in which ssh.c sets an
    'input_eof' flag in the BPP, and that responds by checking a flag that
    tells it whether to report the EOF as an error or not. (This is the
    main reason for those new BPP_READ / BPP_WAITFOR macros - they can
    check the EOF flag every time the coroutine is resumed.)
    
    Secondly, the error reporting itself is changed around again. I'd
    expected to put some data fields in the public PacketProtocolLayer
    structure that it could set to report errors in the same way as the
    BPPs have been doing, but in the end, I decided propagating all those
    data fields around was a pain and that even the BPPs shouldn't have
    been doing it that way. So I've reverted to a system where everything
    calls back to functions in ssh.c itself to report any connection-
    ending condition. But there's a new family of those functions,
    categorising the possible such conditions by semantics, and each one
    has a different set of detailed effects (e.g. how rudely to close the
    network connection, what exit status should be passed back to the
    whole application, whether to send a disconnect message and/or display
    a GUI error box).
    
    I don't expect this to be immediately perfect: of course, the code has
    been through a big upheaval, new bugs are expected, and I haven't been
    able to do a full job of testing (e.g. I haven't tested every auth or
    kex method). But I've checked that it _basically_ works - both SSH
    protocols, all the different kinds of forwarding channel, more than
    one auth method, Windows and Linux, connection sharing - and I think
    it's now at the point where the easiest way to find further bugs is to
    let it out into the wild and see what users can spot.