1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00
Commit Graph

21 Commits

Author SHA1 Message Date
Simon Tatham
c1d9da67a2 Pass the calling Socket to plug_log.
This enables plug_log to run query methods on the socket in order to
find out useful information to log. I don't expect it's sensible to do
anything else with it.
2024-06-29 12:00:12 +01:00
Simon Tatham
6fcc7ed728 Formatting: fix a few mis-spaced assignments.
I spotted one of those in the raw backend the other day, and now I've
got round to finding a bunch more and fixing them.
2022-12-28 15:28:36 +00:00
Simon Tatham
9f2e1e6e03 Prevent sending double-EOF in raw backend.
You can't call sk_write_eof() twice on the same socket, because the
second one will fail an assertion. Instead, you're supposed to know
you've already sent EOF, and not try to send it again.

The call to sk_write_eof() in raw_special (triggered by pressing ^D in
GUI PuTTY, for example) sets the flag raw->sent_socket_eof in an
attempt to prevent this. But it doesn't _check_ that flag, so a second
press of ^D can reach that assertion failure.
2022-12-21 15:19:15 +00:00
Simon Tatham
2ae338b407 New plug_closing error type for 'user abort'.
This is generated when setup of a network connection is cancelled by
deliberate user action, namely, pressing ^C or ^D or the like at a
get_userpass_input prompt presented during proxy setup.

It's handled just like normal socket setup errors, except that it
omits the call to seat_connection_fatal, on the grounds that in this
one case of connection-setup failure, the user doesn't need to be
_informed_ that the connection failed - they already know, because
they failed it themself on purpose.
2021-11-06 14:48:26 +00:00
Simon Tatham
0fe41294e6 New API for plug_closing() with a custom type enum.
Passing an operating-system-specific error code to plug_closing(),
such as errno or GetLastError(), was always a bit weird, given that it
generally had to be handled by cross-platform receiving code in
backends. I had the platform.h implementations #define any error
values that the cross-platform code would have to handle specially,
but that's still not a great system, because it also doesn't leave
freedom to invent error representations of my own that don't
correspond to any OS code. (For example, the ones I just removed from
proxy.h.)

So now, the OS error code is gone from the plug_closing API, and in
its place is a custom enumeration of closure types: normal, error, and
the special case BROKEN_PIPE which is the only OS error code we have
so far needed to handle specially. (All others just mean 'abandon the
connection and print the textual message'.)

Having already centralised the handling of OS error codes in the
previous commit, we've now got a convenient place to add any further
type codes for errors needing special handling: each of Unix
plug_closing_errno(), Windows plug_closing_system_error(), and Windows
plug_closing_winsock_error() can easily grow extra special cases if
need be, and each one will only have to live in one place.
2021-11-06 14:48:26 +00:00
Simon Tatham
7460594433 Move TempSeat creation/destruction into Interactor.
Previously, SshProxy dealt with creating a TempSeat to wrap the one it
was borrowing from its client, and then each client in turn dealt with
detecting when it had had its seat borrowed and finishing up with the
TempSeat. The latter involved a lot of code duplication; the former
didn't involve code duplication _yet_ (since SshProxy was the only
thing doing this job), but would have once we started wanting to do
interactive password prompting for other types of network proxy.

Now all of that functionality is centralised into two new Interactor
helper functions: interactor_borrow_seat and interactor_return_seat.
2021-10-30 18:20:58 +01:00
Simon Tatham
89a390bdeb Pass an Interactor to new_connection().
Thanks to the previous commit, this new parameter can replace two of
the existing ones: instead of passing a LogPolicy and a Seat, we now
pass just an Interactor, from which any proxy implementation can
extract the LogPolicy and the Seat anyway if they need it.
2021-10-30 18:19:56 +01:00
Simon Tatham
aac5e096fa Add Interactor methods to get/set LogPolicy and Seat.
Nothing uses this yet, but the next commit will.
2021-10-30 18:19:56 +01:00
Simon Tatham
44db74ec51 Introduce a new 'Interactor' trait.
This trait will be implemented by anything that wants to display
interactive prompts or notifications to the user in the course of
setting up a network connection, _or_ anything that wants to make a
network connection whose proxy setup might in turn need to do that.

To begin with, that means every Backend that makes network connections
at all must be an Interactor, because any of those network connections
might be proxied via an SSH jump host which might need to interact
with the user.

I'll fill in the contents of this trait over the next few commits, to
keep the patches comprehensible. For the moment, I've just introduced
the trait, set up implementations of it in the five network backends,
and given it a single 'description' method.

The previous 'description' methods of Backend and Plug are now
removed, and their work is done by the new Interactor method instead.
(I changed my mind since last week about where that should best live.)
This isn't too much of an upheaval, fortunately, because I hadn't got
round yet to committing anything that used those methods!
2021-10-30 18:19:54 +01:00
Simon Tatham
76dc28552c Add memsets after allocation of all Backend implementors.
Now every struct that implements the Backend trait is completely
cleared before we start initialising any of its fields. This will mean
I can add new fields that default to 0 or NULL, without having to mess
around initialising them explicitly everywhere.
2021-10-30 17:28:28 +01:00
Simon Tatham
f1746d69b1 Add 'description' methods for Backend and Plug.
These will typically be implemented by objects that are both a Backend
*and* a Plug, and the two methods will deliver the same results to any
caller, regardless of which facet of the object is known to that
caller.

Their purpose is to deliver a user-oriented natural-language
description of what network connection the object is handling, so that
it can appear in diagnostic messages.

The messages I specifically have in mind are going to appear in cases
where proxies require interactive authentication: when PuTTY prompts
interactively for a password, it will need to explain which *thing*
it's asking for the password for, and these descriptions are what it
will use to describe the thing in question.

Each backend is allowed to compose these messages however it thinks
best. In all cases at present, the description string is constructed
by the new centralised default_description() function, which takes a
host name and port number and combines them with the backend's display
name. But the SSH backend does things a bit differently, because it
uses the _logical_ host name (the one that goes with the SSH host key)
rather than the physical destination of the network connection. That
seems more appropriate when the question it's really helping the user
to answer is "What host am I supposed to be entering the password for?"

In this commit, no clients of the new methods are introduced. I have a
draft implementation of actually using it for the purpose I describe
above, but it needs polishing.
2021-10-24 10:48:25 +01:00
Simon Tatham
5374444879 Lowercase version of BackendVtable's displayname.
The current 'displayname' field is designed for presenting in the
config UI, so it starts with a capital letter even when it's not a
proper noun. If I want to name the backend in the middle of a
sentence, I'll need a version that starts with lowercase where
appropriate.

The old field is renamed displayname_tc, to avoid ambiguity.
2021-10-24 09:59:05 +01:00
Simon Tatham
d42f1fe96d Remove 'calling_back' parameter from plug_closing.
It was totally unused. No implementation of the 'closing' method in a
Plug vtable was checking it for any reason at all, except for
ProxySocket which captured it from its client in order to pass on to
its server (which, perhaps after further iterations of ProxySocket,
would have ended up ignoring it similarly). And every caller of
plug_closing set it to 0 (aka false), except for the one in sshproxy.c
which passed true (but it would have made no difference to anyone).

The comment in network.h refers to a FIXME comment which was in
try_send() when that code was written (see winnet.c in commit
7b0e082700). That FIXME is long gone, replaced by a use of a
toplevel callback. So I think the aim must have been to avoid
re-entrancy when sk_write called try_send which encountered a socket
error and called back to plug_closing - but that's long since fixed by
other means now.
2021-10-24 09:58:59 +01:00
Simon Tatham
9f0e7d2915 Backends: notify ldisc when sendok becomes true. (NFC)
I've introduced a function ldisc_notify_sendok(), which backends
should call on their ldisc (if they have one) when anything changes
that might cause backend_sendok() to start returning true.

At the moment, the function does nothing. But in future, I'm going to
make ldisc start buffering typed-ahead input data not yet sent to the
backend, and then the effect of this function will be to trigger
flushing all that data into the backend.

Backends only have to call this function if sendok was previously
false: backends requiring no network connection stage (like pty and
serial) can safely return true from sendok, and in that case, they
don't also have to immediately call this function.
2021-09-14 11:23:20 +01:00
Simon Tatham
6d272ee007 Allow new_connection to take an optional Seat. (NFC)
This is working towards allowing the subsidiary SSH connection in an
SshProxy to share the main user-facing Seat, so as to be able to pass
through interactive prompts.

This is more difficult than the similar change with LogPolicy, because
Seats are stateful. In particular, the trust-sigil status will need to
be controlled by the SshProxy until it's ready to pass over control to
the main SSH (or whatever) connection.

To make this work, I've introduced a thing called a TempSeat, which is
(yet) another Seat implementation. When a backend hands its Seat to
new_connection(), it does it in a way that allows new_connection() to
borrow it completely, and replace it in the main backend structure
with a TempSeat, which acts as a temporary placeholder. If the main
backend tries to do things like changing trust status or sending
output, the TempSeat will buffer them; later on, when the connection
is established, TempSeat will replay the changes into the real Seat.

So, in each backend, I've made the following changes:
 - pass &foo->seat to new_connection, which may overwrite it with a
   TempSeat.
 - if it has done so (which we can tell via the is_tempseat() query
   function), then we have to free the TempSeat and reinstate our main
   Seat. The signal that we can do so is the PLUGLOG_CONNECT_SUCCESS
   notification, which indicates that SshProxy has finished all its
   connection setup work.
 - we also have to remember to free the TempSeat if our backend is
   disposed of without that having happened (e.g. because the
   connection _doesn't_ succeed).
 - in backends which have no local auth phase to worry about, ensure
   we don't call seat_set_trust_status on the main Seat _before_ it
   gets potentially replaced with a TempSeat. Moved some calls of
   seat_set_trust_status to just after new_connection(), so that now
   the initial trust status setup will go into the TempSeat (if
   appropriate) and be buffered until that seat is relinquished.

In all other uses of new_connection, where we don't have a Seat
available at all, we just pass NULL.

This is NFC, because neither new_connection() nor any of its delegates
will _actually_ do this replacement yet. We're just setting up the
framework to enable it to do so in the next commit.
2021-09-13 17:24:47 +01:00
Simon Tatham
a08f953bd6 sshproxy: share the caller's LogPolicy.
Now new_connection() takes an optional LogPolicy * argument, and
passes it on to the SshProxy setup. This means that SshProxy's
implementation of the LogPolicy trait can answer queries like
askappend() and logging_error() by passing them on to the same
LogPolicy used by the main backend.

Not all callers of new_connection have a LogPolicy, so we still have
to fall back to the previous conservative default behaviour if
SshProxy doesn't have a LogPolicy it can ask.

The main backend implementations didn't _quite_ have access to a
LogPolicy already, but they do have a LogContext, which has a
LogPolicy vtable pointer inside it; so I've added a query function
log_get_policy() which allows them to extract that pointer to pass to
new_connection.

This is the first step of fixing the non-interactivity limitations of
SshProxy. But it's also the easiest step: the next ones will be more
involved.
2021-09-13 17:18:31 +01:00
Simon Tatham
0b099b6a6f Non-SSH network backends: handle PLUGLOG_CONNECT_SUCCESS.
All four of the other network-protocol backends (Raw, Telnet, rlogin
and SUPDUP) now have a 'socket_connected' flag, which starts off false
and is set to true when (if) their Socket signals that the connection
attempt has succeeded.

This field is used to tell backend_socket_log whether the session has
started yet (hence, whether it should still be logging messages from
the proxy). This replaces various ad-hoc answers to that question in
each backend, which were the best I could do when sockets didn't
notify connection success. Now they do, we can do it properly.

Also, the new flag controls the answer to each backend's sendok()
method, which makes them all satisfy a new policy rule: no backend
shall return true from sendok() while its network connection attempt
is still ongoing.

(Rationale: the network connection attempt may in future involve a
proxy implementation interacting with the user via the terminal, and
it can't do that if the backend is already consuming all the terminal
input.)
2021-09-13 12:06:45 +01:00
Simon Tatham
c336643576 Separate backend_send from backend_sendbuffer.
On a similar theme of separating the query operation from the
attempted change, backend_send() now no longer has the side effect of
returning the current size of the send buffer. Instead, you have to
call backend_sendbuffer() every time you want to know that.
2021-09-12 09:52:46 +01:00
Simon Tatham
6246ff3f0a New Seat callback, seat_sent().
This is used to notify the Seat that some data has been cleared from
the backend's outgoing data buffer. In other words, it notifies the
Seat that it might be worth calling backend_sendbuffer() again.

We've never needed this before, because until now, Seats have always
been the 'main program' part of the application, meaning they were
also in control of the event loop. So they've been able to call
backend_sendbuffer() proactively, every time they go round the event
loop, instead of having to wait for a callback.

But now, the SSH proxy is the first example of a Seat without
privileged access to the event loop, so it has no way to find out that
the backend's sendbuffer has got smaller. And without that, it can't
pass that notification on to plug_sent, to unblock in turn whatever
the proxied connection might have been waiting to send.

In fact, before this commit, sshproxy.c never called plug_sent at all.
As a result, large data uploads over an SSH jump host would hang
forever as soon as the outgoing buffer filled up for the first time:
the main backend (to which sshproxy.c was acting as a Socket) would
carefully stop filling up the buffer, and then never receive the call
to plug_sent that would cause it to start again.

The new callback is ignored everywhere except in sshproxy.c. It might
be a good idea to remove backend_sendbuffer() entirely and convert all
previous uses of it into non-empty implementations of this callback,
so that we've only got one system; but for the moment, I haven't done
that.
2021-06-27 13:52:48 +01:00
Simon Tatham
0553aec60a New Seat method, notify_remote_disconnect.
This notifies the Seat that the entire backend session has finished
and closed its network connection - or rather, that it _might_ have
done, and that the frontend should check backend_connected() if it
wasn't planning to do so already.

The existing Seat implementations haven't needed this: the GUI ones
don't actually need to do anything specific when the network
connection goes away, and the CLI ones deal with it by being in charge
of their own event loop so that they can easily check
backend_connected() at every possible opportunity in any case. But I'm
about to introduce a new Seat implementation that does need to know
this, and doesn't have any other way to get notified of it.
2021-05-22 13:09:34 +01:00
Simon Tatham
419e5e2230 Move other backends into a subdirectory.
This is the last of the subdirectory creations I had planned. This one
is almost too footling to bother with (it hardly declutters the top
level very much).

One useful side effect is that I've included testback.c (containing
the null and loopback backends) in the otherbackends library, which
means it will now actually be _compiled_ even when nothing's using it,
and we'll spot bit-rot promptly when internal APIs change.

(And, to prove the point, I've immediately had to fix some bit-rot.)
2021-04-22 18:24:15 +01:00