Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.


git-svn-id: svn://svn.tartarus.org/sgt/putty@4309 cda61777-01e9-0310-a592-d414129be87e
  • Loading branch information...
commit 79bf227ba7ba02e32ac710621b672e2789f9ef50 1 parent 01fe3d8
jacob authored
View
4 config.c
@@ -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
33 doc/config.but
@@ -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
7 mac/macnet.c
@@ -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
5 mac/macterm.c
@@ -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
2  mac/mtcpnet.c
@@ -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
4 mac/otnet.c
@@ -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
10 network.h
@@ -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
2  plink.c
@@ -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
2  portfwd.c
@@ -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
4 pproxy.c
@@ -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;
}
View
11 proxy.c
@@ -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
3  psftp.c
@@ -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
4 putty.h
@@ -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;
View
5 raw.c
@@ -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
4 rlogin.c
@@ -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;
View
3  scp.c
@@ -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
2  settings.c
@@ -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,
View
9 ssh.c
@@ -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
4 telnet.c
@@ -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
10 testback.c
@@ -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
3  unix/pterm.c
@@ -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
3  unix/pty.c
@@ -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
7 unix/uxnet.c
@@ -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
2  unix/uxplink.c
@@ -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
4 unix/uxproxy.c
@@ -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
3  window.c
@@ -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
1  winhelp.h
@@ -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
7 winnet.c
@@ -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
2  x11fwd.c
@@ -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;
Please sign in to comment.
Something went wrong with that request. Please try again.