mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +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'!
285 lines
11 KiB
C
285 lines
11 KiB
C
/*
|
|
* 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);
|
|
int (*write) (Socket *s, const void *data, int len);
|
|
int (*write_oob) (Socket *s, const void *data, int len);
|
|
void (*write_eof) (Socket *s);
|
|
void (*flush) (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;
|
|
};
|
|
|
|
struct PlugVtable {
|
|
void (*log)(Plug *p, int 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.
|
|
*
|
|
* - type==0 means we are about to try to connect to address
|
|
* `addr' (error_msg and error_code are ignored)
|
|
* - type==1 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.
|
|
* - type==2 means that error_msg contains a line of generic
|
|
* logging information about setting up the connection. This
|
|
* will typically be a wodge of standard-error output from a
|
|
* 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, char *data, int 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, int 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);
|
|
bool proxy_for_destination (SockAddr *addr, const char *hostname, int port,
|
|
Conf *conf);
|
|
|
|
/* 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);
|
|
|
|
#define sk_plug(s,p) (((s)->vt->plug) (s, p))
|
|
#define sk_close(s) (((s)->vt->close) (s))
|
|
#define sk_write(s,buf,len) (((s)->vt->write) (s, buf, len))
|
|
#define sk_write_oob(s,buf,len) (((s)->vt->write_oob) (s, buf, len))
|
|
#define sk_write_eof(s) (((s)->vt->write_eof) (s))
|
|
#define sk_flush(s) (((s)->vt->flush) (s))
|
|
|
|
#define plug_log(p,type,addr,port,msg,code) \
|
|
(((p)->vt->log) (p, type, addr, port, msg, code))
|
|
#define plug_closing(p,msg,code,callback) \
|
|
(((p)->vt->closing) (p, msg, code, callback))
|
|
#define plug_receive(p,urgent,buf,len) \
|
|
(((p)->vt->receive) (p, urgent, buf, len))
|
|
#define plug_sent(p,bufsize) \
|
|
(((p)->vt->sent) (p, bufsize))
|
|
#define plug_accepting(p, constructor, ctx) \
|
|
(((p)->vt->accepting)(p, constructor, 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);
|
|
#define sk_socket_error(s) (((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.
|
|
*/
|
|
#define sk_set_frozen(s, 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.
|
|
*/
|
|
#define sk_peer_info(s) (((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.
|
|
*/
|
|
Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...);
|
|
|
|
/*
|
|
* 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,
|
|
int type, SockAddr *addr, int port,
|
|
const char *error_msg, int error_code, Conf *conf,
|
|
bool session_started);
|
|
void log_proxy_stderr(Plug *plug, bufchain *buf, const void *vdata, int len);
|
|
|
|
#endif
|