'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)));
}
}
-\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
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
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}
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);
}
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;
}
-/* $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
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);
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,
}
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,
return (Socket) ret;
}
- /* TODO: oobinline, nodelay */
+ /* TODO: oobinline, nodelay, keepalive */
/*
* Bind to local address.
* 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,
/* (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 */
/* 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);
(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;
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;
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;
}
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,
Socket sret;
if ((sret = platform_new_connection(addr, hostname, port, privport,
- oobinline, nodelay, plug, cfg)) !=
+ oobinline, nodelay, keepalive,
+ plug, cfg)) !=
NULL)
return sret;
*/
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;
}
/* 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,
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;
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);
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;
*/
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,
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;
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,
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;
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);
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);
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,
* 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,
}
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;
*/
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;
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;
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,
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;
-/* $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
#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 *);
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;
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",
* 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;
}
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
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.
*/
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;
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;
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);
#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"
}
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,
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.
*/
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;