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

Add a configuration option for TCP keepalives (SO_KEEPALIVE), default off.

No very good reason, but I've occasionally wanted to frob it to see if it
makes any difference to problems I'm having, and it was easy.

Tested that it does actually cause keepalives on Windows (with tcpdump);
should also work on Unix. Not implemented on Mac (does nothing), but then
neither is TCP_NODELAY.

Quite a big checkin, much of which is adding `keepalive' alongside `nodelay'
in network function calls.

[originally from svn r4309]
This commit is contained in:
Jacob Nevins 2004-06-20 17:07:38 +00:00
parent 6bcac21e36
commit 20f433efac
29 changed files with 109 additions and 51 deletions

View File

@ -1292,6 +1292,10 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
'n', HELPCTX(connection_nodelay),
dlg_stdcheckbox_handler,
I(offsetof(Config,tcp_nodelay)));
ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
'p', HELPCTX(connection_tcpkeepalive),
dlg_stdcheckbox_handler,
I(offsetof(Config,tcp_keepalives)));
}
}

View File

@ -1,4 +1,4 @@
\versionid $Id: config.but,v 1.83 2004/06/15 11:31:30 jacob Exp $
\versionid $Id: config.but,v 1.84 2004/06/20 17:07:36 jacob Exp $
\C{config} Configuring PuTTY
@ -1506,7 +1506,8 @@ what \e{kind} of network problems you have between you and the
server.
Keepalives are only supported in Telnet and SSH; the Rlogin and Raw
protocols offer no way of implementing them.
protocols offer no way of implementing them. (For an alternative, see
\k{config-tcp-keepalives}.)
Note that if you are using SSH1 and the server has a bug that makes
it unable to deal with SSH1 ignore messages (see
@ -1525,6 +1526,34 @@ types of server.
The Nagle algorithm is disabled by default.
\S{config-tcp-keepalives} \q{Enable TCP keepalives}
\cfg{winhelp-topic}{connection.tcpkeepalive}
\e{NOTE:} TCP keepalives should not be confused with the
application-level keepalives described in \k{config-keepalive}. If in
doubt, you probably want application-level keepalives; TCP keepalives
are provided for completeness.
The idea of TCP keepalives is similar to application-level keepalives,
and the same caveats apply. The main differences are:
\b TCP keepalives are available on \e{all} connection types, including
Raw and Rlogin.
\b The interval between TCP keepalives is usually much longer,
typically two hours; this is set by the operating system, and cannot
be configured within PuTTY.
\b If the operating system does not receive a response to a keepalive,
it may send out more in quick succession and if terminate the connection
if no response is received.
TCP keepalives may be useful for ensuring that half-open connections
are terminated than for keeping a connection alive.
TCP keepalives are disabled by default.
\H{config-proxy} The Proxy panel
\cfg{winhelp-topic}{proxy.main}

View File

@ -12,7 +12,7 @@ struct macnet_stack {
void (*addrcopy)(SockAddr, char *);
void (*addr_free)(SockAddr);
Socket (*skregister)(void *, Plug); /* "register" is a reserved word */
Socket (*new)(SockAddr, int, int, int, int, Plug);
Socket (*new)(SockAddr, int, int, int, int, int, Plug);
Socket (*newlistener)(char *, int, Plug, int);
char *(*addr_error)(SockAddr);
void (*poll)(void);
@ -128,11 +128,12 @@ Socket sk_register(void *sock, Plug plug)
}
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug plug)
int nodelay, int keepalive, Plug plug)
{
if (stack != NULL)
return stack->new(addr, port, privport, oobinline, nodelay, plug);
return stack->new(addr, port, privport, oobinline, nodelay, keepalive,
plug);
return NULL;
}

View File

@ -1,4 +1,4 @@
/* $Id: macterm.c,v 1.75 2003/05/04 14:18:18 simon Exp $ */
/* $Id: macterm.c,v 1.76 2004/06/20 17:07:37 jacob Exp $ */
/*
* Copyright (c) 1999 Simon Tatham
* Copyright (c) 1999, 2002 Ben Harris
@ -158,7 +158,8 @@ void mac_startsession(Session *s)
term_provide_logctx(s->term, s->logctx);
errmsg = s->back->init(s, &s->backhandle, &s->cfg, s->cfg.host,
s->cfg.port, &s->realhost, s->cfg.tcp_nodelay);
s->cfg.port, &s->realhost, s->cfg.tcp_nodelay,
s->cfg.tcp_keepalives);
if (errmsg != NULL)
fatalbox("%s", errmsg);
s->back->provide_logctx(s->backhandle, s->logctx);

View File

@ -410,7 +410,7 @@ Socket mactcp_register(void *sock, Plug plug)
static TCPNotifyUPP mactcp_asr_upp;
Socket mactcp_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug plug)
int nodelay, int keepalive, Plug plug)
{
static struct socket_function_table fn_table = {
mactcp_plug,

View File

@ -234,7 +234,7 @@ Socket ot_register(void *sock, Plug plug)
}
Socket ot_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug plug)
int nodelay, int keepalive, Plug plug)
{
static struct socket_function_table fn_table = {
ot_tcp_plug,
@ -281,7 +281,7 @@ Socket ot_new(SockAddr addr, int port, int privport, int oobinline,
return (Socket) ret;
}
/* TODO: oobinline, nodelay */
/* TODO: oobinline, nodelay, keepalive */
/*
* Bind to local address.

View File

@ -79,8 +79,8 @@ struct plug_function_table {
* responsibility for freeing it */
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug,
const Config *cfg);
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg);
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
const Config *cfg);
SockAddr name_lookup(char *host, int port, char **canonicalname,
@ -90,8 +90,8 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
/* (same caveat about addr as new_connection()) */
Socket platform_new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug,
const Config *cfg);
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg);
/* socket functions */
@ -111,7 +111,7 @@ void sk_addr_free(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, int privport, int oobinline,
int nodelay, Plug p);
int nodelay, int keepalive, Plug p);
Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only);

View File

@ -564,7 +564,7 @@ int main(int argc, char **argv)
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
&realhost, nodelay);
&realhost, nodelay, cfg.tcp_keepalives);
if (error) {
fprintf(stderr, "Unable to open connection:\n%s", error);
return 1;

View File

@ -390,7 +390,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
pr->dynamic = 0;
pr->s = *s = new_connection(addr, dummy_realhost, port,
0, 1, 0, (Plug) pr, cfg);
0, 1, 0, 0, (Plug) pr, cfg);
if ((err = sk_socket_error(*s)) != NULL) {
sfree(pr);
return err;

View File

@ -10,8 +10,8 @@
Socket platform_new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug,
const Config *cfg)
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg)
{
return NULL;
}

11
proxy.c
View File

@ -356,8 +356,8 @@ SockAddr name_lookup(char *host, int port, char **canonicalname,
Socket new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug,
const Config *cfg)
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg)
{
static const struct socket_function_table socket_fn_table = {
sk_proxy_plug,
@ -388,7 +388,8 @@ Socket new_connection(SockAddr addr, char *hostname,
Socket sret;
if ((sret = platform_new_connection(addr, hostname, port, privport,
oobinline, nodelay, plug, cfg)) !=
oobinline, nodelay, keepalive,
plug, cfg)) !=
NULL)
return sret;
@ -444,7 +445,7 @@ Socket new_connection(SockAddr addr, char *hostname,
*/
ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port,
privport, oobinline,
nodelay, (Plug) pplug);
nodelay, keepalive, (Plug) pplug);
if (sk_socket_error(ret->sub_socket) != NULL)
return (Socket) ret;
@ -456,7 +457,7 @@ Socket new_connection(SockAddr addr, char *hostname,
}
/* no proxy, so just return the direct socket */
return sk_new(addr, port, privport, oobinline, nodelay, plug);
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
}
Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,

View File

@ -1939,7 +1939,8 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
back = &ssh_backend;
err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,0);
err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,
0, cfg.tcp_keepalives);
if (err != NULL) {
fprintf(stderr, "ssh_init: %s\n", err);
return 1;

View File

@ -269,7 +269,8 @@ enum {
struct backend_tag {
const char *(*init) (void *frontend_handle, void **backend_handle,
Config *cfg,
char *host, int port, char **realhost, int nodelay);
char *host, int port, char **realhost, int nodelay,
int keepalive);
void (*free) (void *handle);
/* back->reconfig() passes in a replacement configuration. */
void (*reconfig) (void *handle, Config *cfg);
@ -329,6 +330,7 @@ struct config_tag {
int warn_on_close;
int ping_interval; /* in seconds */
int tcp_nodelay;
int tcp_keepalives;
/* Proxy options */
char proxy_exclude_list[512];
int proxy_dns;

5
raw.c
View File

@ -69,7 +69,8 @@ static void raw_sent(Plug plug, int bufsize)
*/
static const char *raw_init(void *frontend_handle, void **backend_handle,
Config *cfg,
char *host, int port, char **realhost, int nodelay)
char *host, int port, char **realhost, int nodelay,
int keepalive)
{
static const struct plug_function_table fn_table = {
raw_closing,
@ -115,7 +116,7 @@ static const char *raw_init(void *frontend_handle, void **backend_handle,
logevent(raw->frontend, buf);
sfree(buf);
}
raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay,
raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive,
(Plug) raw, cfg);
if ((err = sk_socket_error(raw->s)) != NULL)
return err;

View File

@ -100,7 +100,7 @@ static void rlogin_sent(Plug plug, int bufsize)
static const char *rlogin_init(void *frontend_handle, void **backend_handle,
Config *cfg,
char *host, int port, char **realhost,
int nodelay)
int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
rlogin_closing,
@ -149,7 +149,7 @@ static const char *rlogin_init(void *frontend_handle, void **backend_handle,
sfree(buf);
}
rlogin->s = new_connection(addr, *realhost, port, 1, 0,
nodelay, (Plug) rlogin, cfg);
nodelay, keepalive, (Plug) rlogin, cfg);
if ((err = sk_socket_error(rlogin->s)) != NULL)
return err;

3
scp.c
View File

@ -450,7 +450,8 @@ static void do_cmd(char *host, char *user, char *cmd)
back = &ssh_backend;
err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,0);
err = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port, &realhost,
0, cfg.tcp_keepalives);
if (err != NULL)
bump("ssh_init: %s", err);
logctx = log_init(NULL, &cfg);

View File

@ -182,6 +182,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */
write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60); /* seconds */
write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);
write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives);
write_setting_s(sesskey, "TerminalType", cfg->termtype);
write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);
@ -411,6 +412,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
cfg->ping_interval = pingmin * 60 + pingsec;
}
gppi(sesskey, "TCPNoDelay", 1, &cfg->tcp_nodelay);
gppi(sesskey, "TCPKeepalives", 0, &cfg->tcp_keepalives);
gpps(sesskey, "TerminalType", "xterm", cfg->termtype,
sizeof(cfg->termtype));
gpps(sesskey, "TerminalSpeed", "38400,38400", cfg->termspeed,

9
ssh.c
View File

@ -2128,7 +2128,7 @@ static void ssh_sent(Plug plug, int bufsize)
* freed by the caller.
*/
static const char *connect_to_host(Ssh ssh, char *host, int port,
char **realhost, int nodelay)
char **realhost, int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
ssh_closing,
@ -2169,7 +2169,7 @@ static const char *connect_to_host(Ssh ssh, char *host, int port,
}
ssh->fn = &fn_table;
ssh->s = new_connection(addr, *realhost, port,
0, 1, nodelay, (Plug) ssh, &ssh->cfg);
0, 1, nodelay, keepalive, (Plug) ssh, &ssh->cfg);
if ((err = sk_socket_error(ssh->s)) != NULL) {
ssh->s = NULL;
return err;
@ -6183,7 +6183,8 @@ static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
*/
static const char *ssh_init(void *frontend_handle, void **backend_handle,
Config *cfg,
char *host, int port, char **realhost, int nodelay)
char *host, int port, char **realhost, int nodelay,
int keepalive)
{
const char *p;
Ssh ssh;
@ -6267,7 +6268,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->protocol = NULL;
p = connect_to_host(ssh, host, port, realhost, nodelay);
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
if (p != NULL)
return p;

View File

@ -674,7 +674,7 @@ static void telnet_sent(Plug plug, int bufsize)
static const char *telnet_init(void *frontend_handle, void **backend_handle,
Config *cfg,
char *host, int port, char **realhost,
int nodelay)
int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
telnet_closing,
@ -729,7 +729,7 @@ static const char *telnet_init(void *frontend_handle, void **backend_handle,
sfree(buf);
}
telnet->s = new_connection(addr, *realhost, port, 0, 1,
nodelay, (Plug) telnet, &telnet->cfg);
nodelay, keepalive, (Plug) telnet, &telnet->cfg);
if ((err = sk_socket_error(telnet->s)) != NULL)
return err;

View File

@ -1,4 +1,4 @@
/* $Id: testback.c,v 1.9 2003/05/10 11:57:55 ben Exp $ */
/* $Id: testback.c,v 1.10 2004/06/20 17:07:32 jacob Exp $ */
/*
* Copyright (c) 1999 Simon Tatham
* Copyright (c) 1999 Ben Harris
@ -34,9 +34,9 @@
#include "putty.h"
static const char *null_init(void *, void **, Config *, char *, int, char **,
int);
int, int);
static const char *loop_init(void *, void **, Config *, char *, int, char **,
int);
int, int);
static void null_free(void *);
static void loop_free(void *);
static void null_reconfig(void *, Config *);
@ -72,14 +72,14 @@ struct loop_state {
static const char *null_init(void *frontend_handle, void **backend_handle,
Config *cfg, char *host, int port,
char **realhost, int nodelay) {
char **realhost, int nodelay, int keepalive) {
return NULL;
}
static const char *loop_init(void *frontend_handle, void **backend_handle,
Config *cfg, char *host, int port,
char **realhost, int nodelay) {
char **realhost, int nodelay, int keepalive) {
struct loop_state *st = snew(struct loop_state);
st->term = frontend_handle;

View File

@ -3367,7 +3367,8 @@ int pt_main(int argc, char **argv)
error = inst->back->init((void *)inst, &inst->backhandle,
&inst->cfg, inst->cfg.host, inst->cfg.port,
&realhost, inst->cfg.tcp_nodelay);
&realhost, inst->cfg.tcp_nodelay,
inst->cfg.tcp_keepalives);
if (error) {
char *msg = dupprintf("Unable to open connection to %s:\n%s",

View File

@ -486,7 +486,8 @@ static void pty_uxsel_setup(void)
* freed by the caller.
*/
static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
char *host, int port, char **realhost, int nodelay)
char *host, int port, char **realhost, int nodelay,
int keepalive)
{
int slavefd;
pid_t pid, pgrp;

View File

@ -378,7 +378,7 @@ Socket sk_register(OSSocket sockfd, Plug plug)
}
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug plug)
int nodelay, int keepalive, Plug plug)
{
int s;
#ifdef IPV6
@ -433,6 +433,11 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));
}
if (keepalive) {
int b = TRUE;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));
}
/*
* Bind to local address.
*/

View File

@ -557,7 +557,7 @@ int main(int argc, char **argv)
int nodelay = cfg.tcp_nodelay && isatty(0);
error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
&realhost, nodelay);
&realhost, nodelay, cfg.tcp_keepalives);
if (error) {
fprintf(stderr, "Unable to open connection:\n%s\n", error);
return 1;

View File

@ -221,8 +221,8 @@ static int localproxy_select_result(int fd, int event)
Socket platform_new_connection(SockAddr addr, char *hostname,
int port, int privport,
int oobinline, int nodelay, Plug plug,
const Config *cfg)
int oobinline, int nodelay, int keepalive,
Plug plug, const Config *cfg)
{
char *cmd;

View File

@ -611,7 +611,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
char *realhost;
error = back->init(NULL, &backhandle, &cfg,
cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
cfg.tcp_keepalives);
back->provide_logctx(backhandle, logctx);
if (error) {
char *str = dupprintf("%s Error", appname);

View File

@ -63,6 +63,7 @@
#define WINHELP_CTX_connection_username "connection.username"
#define WINHELP_CTX_connection_keepalive "connection.keepalive"
#define WINHELP_CTX_connection_nodelay "connection.nodelay"
#define WINHELP_CTX_connection_tcpkeepalive "connection.tcpkeepalive"
#define WINHELP_CTX_proxy_type "proxy.type"
#define WINHELP_CTX_proxy_main "proxy.main"
#define WINHELP_CTX_proxy_exclude "proxy.exclude"

View File

@ -658,7 +658,7 @@ Socket sk_register(void *sock, Plug plug)
}
Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
int nodelay, Plug plug)
int nodelay, int keepalive, Plug plug)
{
static const struct socket_function_table fn_table = {
sk_tcp_plug,
@ -722,6 +722,11 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
p_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));
}
if (keepalive) {
BOOL b = TRUE;
p_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));
}
/*
* Bind to local address.
*/

View File

@ -310,7 +310,7 @@ const char *x11_init(Socket * s, char *display, void *c, void *auth,
pr->c = c;
pr->s = *s = new_connection(addr, dummy_realhost, port,
0, 1, 0, (Plug) pr, cfg);
0, 1, 0, 0, (Plug) pr, cfg);
if ((err = sk_socket_error(*s)) != NULL) {
sfree(pr);
return err;