mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
3214563d8e
My normal habit these days, in new code, is to treat int and bool as _almost_ completely separate types. I'm still willing to use C's implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine, no need to spell it out as blob.len != 0), but generally, if a variable is going to be conceptually a boolean, I like to declare it bool and assign to it using 'true' or 'false' rather than 0 or 1. PuTTY is an exception, because it predates the C99 bool, and I've stuck to its existing coding style even when adding new code to it. But it's been annoying me more and more, so now that I've decided C99 bool is an acceptable thing to require from our toolchain in the first place, here's a quite thorough trawl through the source doing 'boolification'. Many variables and function parameters are now typed as bool rather than int; many assignments of 0 or 1 to those variables are now spelled 'true' or 'false'. I managed this thorough conversion with the help of a custom clang plugin that I wrote to trawl the AST and apply heuristics to point out where things might want changing. So I've even managed to do a decent job on parts of the code I haven't looked at in years! To make the plugin's work easier, I pushed platform front ends generally in the direction of using standard 'bool' in preference to platform-specific boolean types like Windows BOOL or GTK's gboolean; I've left the platform booleans in places they _have_ to be for the platform APIs to work right, but variables only used by my own code have been converted wherever I found them. In a few places there are int values that look very like booleans in _most_ of the places they're used, but have a rarely-used third value, or a distinction between different nonzero values that most users don't care about. In these cases, I've _removed_ uses of 'true' and 'false' for the return values, to emphasise that there's something more subtle going on than a simple boolean answer: - the 'multisel' field in dialog.h's list box structure, for which the GTK front end in particular recognises a difference between 1 and 2 but nearly everything else treats as boolean - the 'urgent' parameter to plug_receive, where 1 vs 2 tells you something about the specific location of the urgent pointer, but most clients only care about 0 vs 'something nonzero' - the return value of wc_match, where -1 indicates a syntax error in the wildcard. - the return values from SSH-1 RSA-key loading functions, which use -1 for 'wrong passphrase' and 0 for all other failures (so any caller which already knows it's not loading an _encrypted private_ key can treat them as boolean) - term->esc_query, and the 'query' parameter in toggle_mode in terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h, but can also hold -1 for some other intervening character that we don't support. In a few places there's an integer that I haven't turned into a bool even though it really _can_ only take values 0 or 1 (and, as above, tried to make the call sites consistent in not calling those values true and false), on the grounds that I thought it would make it more confusing to imply that the 0 value was in some sense 'negative' or bad and the 1 positive or good: - the return value of plug_accepting uses the POSIXish convention of 0=success and nonzero=error; I think if I made it bool then I'd also want to reverse its sense, and that's a job for a separate piece of work. - the 'screen' parameter to lineptr() in terminal.c, where 0 and 1 represent the default and alternate screens. There's no obvious reason why one of those should be considered 'true' or 'positive' or 'success' - they're just indices - so I've left it as int. ssh_scp_recv had particularly confusing semantics for its previous int return value: its call sites used '<= 0' to check for error, but it never actually returned a negative number, just 0 or 1. Now the function and its call sites agree that it's a bool. In a couple of places I've renamed variables called 'ret', because I don't like that name any more - it's unclear whether it means the return value (in preparation) for the _containing_ function or the return value received from a subroutine call, and occasionally I've accidentally used the same variable for both and introduced a bug. So where one of those got in my way, I've renamed it to 'toret' or 'retd' (the latter short for 'returned') in line with my usual modern practice, but I haven't done a thorough job of finding all of them. Finally, one amusing side effect of doing this is that I've had to separate quite a few chained assignments. It used to be perfectly fine to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a the 'true' defined by stdbool.h, that idiom provokes a warning from gcc: 'suggest parentheses around assignment used as truth value'!
150 lines
6.0 KiB
C
150 lines
6.0 KiB
C
/*
|
|
* Abstraction of the binary packet protocols used in SSH.
|
|
*/
|
|
|
|
#ifndef PUTTY_SSHBPP_H
|
|
#define PUTTY_SSHBPP_H
|
|
|
|
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);
|
|
};
|
|
|
|
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;
|
|
|
|
int remote_bugs;
|
|
|
|
/* 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;
|
|
};
|
|
|
|
#define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp))
|
|
#define ssh_bpp_handle_output(bpp) ((bpp)->vt->handle_output(bpp))
|
|
#define ssh_bpp_new_pktout(bpp, type) ((bpp)->vt->new_pktout(type))
|
|
#define ssh_bpp_queue_disconnect(bpp, msg, cat) \
|
|
((bpp)->vt->queue_disconnect(bpp, msg, cat))
|
|
|
|
/* 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 struct ssh1_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, and
|
|
* takes a double pair of parens because it passes a whole argument
|
|
* list to dupprintf. */
|
|
#define bpp_logevent(params) ( \
|
|
logevent_and_free((bpp)->logctx, dupprintf params))
|
|
|
|
/*
|
|
* 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 macro DTS_CONSUME subtracts a given amount
|
|
* from the counter in a particular direction, and evaluates to a
|
|
* boolean indicating whether 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 DataTransferStats {
|
|
struct {
|
|
bool running;
|
|
unsigned long remaining;
|
|
} in, out;
|
|
};
|
|
#define DTS_CONSUME(stats, direction, size) \
|
|
((stats)->direction.running && \
|
|
(stats)->direction.remaining <= (size) ? \
|
|
((stats)->direction.running = false, true) : \
|
|
((stats)->direction.remaining -= (size), false))
|
|
|
|
BinaryPacketProtocol *ssh2_bpp_new(
|
|
LogContext *logctx, struct DataTransferStats *stats, bool is_server);
|
|
void ssh2_bpp_new_outgoing_crypto(
|
|
BinaryPacketProtocol *bpp,
|
|
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
|
|
const struct ssh2_macalg *mac, bool etm_mode, const void *mac_key,
|
|
const struct ssh_compression_alg *compression, bool delayed_compression);
|
|
void ssh2_bpp_new_incoming_crypto(
|
|
BinaryPacketProtocol *bpp,
|
|
const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
|
|
const struct ssh2_macalg *mac, bool etm_mode, const void *mac_key,
|
|
const struct 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 */
|