*** clientloop.c.orig	Tue Mar  6 12:34:40 2001
--- clientloop.c	Sun Apr  8 15:57:42 2001
***************
*** 126,131 ****
--- 126,132 ----
  static u_int buffer_high;/* Soft max buffer size. */
  static int connection_in;	/* Connection to server (input). */
  static int connection_out;	/* Connection to server (output). */
+ static time_t idle_time_last;	/* Last time of packet transmission. */
  
  void	client_init_dispatch(void);
  int	session_ident = -1;
***************
*** 365,370 ****
--- 366,374 ----
  client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
      int *maxfdp)
  {
+ 	int ret;
+ 	struct timeval tv;
+ 
  	/* Add any selections by the channel mechanism. */
  	channel_prepare_select(readsetp, writesetp, maxfdp);
  
***************
*** 403,409 ****
  	 * SSH_MSG_IGNORE packet when the timeout expires.
  	 */
  
! 	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
  		char buf[100];
  
  		/*
--- 407,417 ----
  	 * SSH_MSG_IGNORE packet when the timeout expires.
  	 */
  
! 	tv.tv_sec = 0;
! 	tv.tv_usec = 500 * 1000;	/* time slot is 0.5sec */
! 
! 	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, &tv);
! 	if (ret < 0) {
  		char buf[100];
  
  		/*
***************
*** 421,426 ****
--- 429,466 ----
  		buffer_append(&stderr_buffer, buf, strlen(buf));
  		quit_pending = 1;
  	}
+ 
+ 	/* If the output channel has been silent for more than a specified
+ 	 * time, send a keepalive packet (heartbeat) to the peer (server).
+ 	 * Keepalive packet is useful for keeping the connection over
+ 	 * IP masquerade (NAT) boxes, firewalls, etc.
+ 	 * Some servers equipped with a watchdog timer require keepalive
+ 	 * packets (heartbeats) to detect link down.
+ 	 *
+ 	 * Note: Although the interval between keepalive packets is not
+ 	 * very precise, it's okay.
+ 	 *
+ 	 * Note: Some old servers may crash when they receive SSH_MSG_IGNORE.
+ 	 * Those who want to connect to such a server should turn this
+ 	 * function off by the option setting (e.g. HeartbeatInterval 0).
+ 	 */
+ 	if (options.heartbeat_interval > 0) {
+ 		if (FD_ISSET(connection_out,*writesetp)) {
+ 			/* Update the time of last data transmission. */
+ 			idle_time_last = time(NULL);
+ 		}
+ 		if (time(NULL) - idle_time_last >= (int)options.heartbeat_interval){
+ 			if (compat20) {
+ 				packet_start(SSH2_MSG_IGNORE);
+ 			}
+ 			else {
+ 				packet_start(SSH_MSG_IGNORE);
+ 			}
+ 			packet_put_string("", 0);
+ 			packet_send();
+ 			/* fputs("*",stderr); */
+ 		}
+ 	}
  }
  
  void
***************
*** 793,798 ****
--- 833,839 ----
  	debug("Entering interactive session.");
  
  	start_time = get_current_time();
+ 	idle_time_last = time(NULL);
  
  	/* Initialize variables. */
  	escape_pending = 0;
*** readconf.c.orig	Tue Mar 20 07:15:57 2001
--- readconf.c	Sat Apr  7 23:55:55 2001
***************
*** 110,116 ****
  	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
  	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
  	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
! 	oPreferredAuthentications
  } OpCodes;
  
  /* Textual representations of the tokens. */
--- 110,116 ----
  	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
  	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
  	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
! 	oPreferredAuthentications, oHeartbeatInterval
  } OpCodes;
  
  /* Textual representations of the tokens. */
***************
*** 170,175 ****
--- 170,176 ----
  	{ "compression", oCompression },
  	{ "compressionlevel", oCompressionLevel },
  	{ "keepalive", oKeepAlives },
+ 	{ "heartbeatinterval", oHeartbeatInterval },
  	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
  	{ "loglevel", oLogLevel },
  	{ "preferredauthentications", oPreferredAuthentications },
***************
*** 387,392 ****
--- 388,397 ----
  		intptr = &options->keepalives;
  		goto parse_flag;
  
+ 	case oHeartbeatInterval:
+ 		intptr = &options->heartbeat_interval;
+ 		goto parse_int;
+ 
  	case oNumberOfPasswordPrompts:
  		intptr = &options->number_of_password_prompts;
  		goto parse_int;
***************
*** 707,712 ****
--- 712,718 ----
  	options->strict_host_key_checking = -1;
  	options->compression = -1;
  	options->keepalives = -1;
+ 	options->heartbeat_interval = -1;
  	options->compression_level = -1;
  	options->port = -1;
  	options->connection_attempts = -1;
***************
*** 791,796 ****
--- 797,804 ----
  		options->compression = 0;
  	if (options->keepalives == -1)
  		options->keepalives = 1;
+ 	if (options->heartbeat_interval == -1)
+ 		options->heartbeat_interval = 0;	/* 0 means "no heartbeat" */
  	if (options->compression_level == -1)
  		options->compression_level = 6;
  	if (options->port == -1)
*** readconf.h.orig	Sun Mar 11 10:49:20 2001
--- readconf.h	Sat Apr  7 23:51:30 2001
***************
*** 61,66 ****
--- 61,69 ----
  	int     compression_level;	/* Compression level 1 (fast) to 9
  					 * (best). */
  	int     keepalives;	/* Set SO_KEEPALIVE. */
+ 	int	heartbeat_interval;	/* Number of seconds between keepalive
+ 					 * packets (heartbeats) over encrypted
+ 					 * channel. (in secs.) */
  	LogLevel log_level;	/* Level for logging. */
  
  	int     port;		/* Port to connect. */
*** serverloop.c.orig	Sat Mar 17 09:47:55 2001
--- serverloop.c	Sun Apr  8 22:34:37 2001
***************
*** 74,79 ****
--- 74,80 ----
  static int connection_in;	/* Connection to client (input). */
  static int connection_out;	/* Connection to client (output). */
  static u_int buffer_high;/* "Soft" max buffer size. */
+ static time_t idle_time_last;   /* Last time of packet receipt. */
  
  /*
   * This SIGCHLD kludge is used to detect when the child exits.  The server
***************
*** 83,88 ****
--- 84,90 ----
  static pid_t child_pid;			/* Pid of the child. */
  static volatile int child_terminated;	/* The child has terminated. */
  static volatile int child_wait_status;	/* Status from wait(). */
+ static int child_forced_to_terminate;	/* The child has been killed by sshd. */
  
  void	server_init_dispatch(void);
  
***************
*** 184,190 ****
      u_int max_time_milliseconds)
  {
  	struct timeval tv, *tvp;
! 	int ret;
  
  	/* When select fails we restart from here. */
  retry_select:
--- 186,192 ----
      u_int max_time_milliseconds)
  {
  	struct timeval tv, *tvp;
! 	int ret, i;
  
  	/* When select fails we restart from here. */
  retry_select:
***************
*** 237,242 ****
--- 239,253 ----
  		if (max_time_milliseconds == 0)
  			max_time_milliseconds = 100;
  
+ 	/* When the watchdog is needed, set the maximum length
+ 	 * of timeout to 0.25sec.
+ 	 */
+ 	if (options.watchdog_timeout > 0) {
+ 		if (max_time_milliseconds == 0 || max_time_milliseconds > 250) {
+ 			max_time_milliseconds = 250;
+ 		}
+ 	}
+ 
  	if (max_time_milliseconds == 0)
  		tvp = NULL;
  	else {
***************
*** 256,261 ****
--- 267,298 ----
  		else
  			goto retry_select;
  	}
+ 
+ 	/* Watchdog timer:
+ 	 * If the input channel has been silent for more than the specified
+ 	 * time, try to kill the child process(es) to protect server resources.
+ 	 */
+ 				/* SSH2 has not been supported yet. */
+ 	if (options.watchdog_timeout > 0 && !compat20) {
+ 		if (FD_ISSET(connection_in,*readsetp)) {
+ 			/* Update the time of last data receipt. */
+                         idle_time_last = time(NULL);
+ 			/* fputs("*",stderr); */
+ 		}
+ 		if (!child_terminated && \
+ 		    (time(NULL) - idle_time_last) > (int)options.watchdog_timeout) {
+ 			/* We won't have child_pid=0, however, for safety... */
+ 			if ( child_pid != 0 ){
+ 				kill(child_pid, SIGHUP);
+ 				child_forced_to_terminate = 1;
+ 				for (i=0 ; i<5 ; i++){
+ 					sleep(1);
+ 					if (child_terminated)  break;
+ 				}
+ 				if (i>=5)  kill(child_pid, SIGKILL);
+ 			}
+ 		}
+ 	}
  }
  
  /*
***************
*** 419,424 ****
--- 456,462 ----
  	/* Initialize the SIGCHLD kludge. */
  	child_pid = pid;
  	child_terminated = 0;
+ 	child_forced_to_terminate = 0;
  	signal(SIGCHLD, sigchld_handler);
  	signal(SIGPIPE, SIG_IGN);
  
***************
*** 471,476 ****
--- 509,516 ----
  
  	server_init_dispatch();
  
+ 	idle_time_last = time(NULL);
+ 
  	/* Main loop of the server for the interactive session mode. */
  	for (;;) {
  
***************
*** 543,548 ****
--- 583,593 ----
  		wait_until_can_do_something(&readset, &writeset, &max_fd,
  		    max_time_milliseconds);
  
+ 		/* If the child has been forced to terminate by this server, break. */
+ 		if (child_forced_to_terminate) {
+ 			break;
+ 		}
+ 
  		/* Process any channel events. */
  		channel_after_select(readset, writeset);
  
***************
*** 560,566 ****
  	/* Cleanup and termination code. */
  
  	/* Wait until all output has been sent to the client. */
! 	drain_output();
  
  	debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
  	      stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
--- 605,613 ----
  	/* Cleanup and termination code. */
  
  	/* Wait until all output has been sent to the client. */
! 	if (!child_forced_to_terminate) {
! 		drain_output();
! 	}
  
  	debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
  	      stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
***************
*** 657,662 ****
--- 704,710 ----
  	mysignal(SIGCHLD, sigchld_handler2);
  	signal(SIGPIPE, SIG_IGN);
  	child_terminated = 0;
+ 	child_forced_to_terminate = 0;
  	connection_in = packet_get_connection_in();
  	connection_out = packet_get_connection_out();
  
***************
*** 663,668 ****
--- 711,718 ----
  	max_fd = MAX(connection_in, connection_out);
  
  	server_init_dispatch();
+ 
+ 	idle_time_last = time(NULL);
  
  	for (;;) {
  		process_buffered_input_packets();
*** servconf.c.orig	Tue Mar  6 10:02:41 2001
--- servconf.c	Sat Apr  7 23:56:22 2001
***************
*** 61,66 ****
--- 61,67 ----
  	options->xauth_location = NULL;
  	options->strict_modes = -1;
  	options->keepalives = -1;
+ 	options->watchdog_timeout = -1;
  	options->log_facility = (SyslogFacility) - 1;
  	options->log_level = (LogLevel) - 1;
  	options->rhosts_authentication = -1;
***************
*** 144,149 ****
--- 145,152 ----
  		options->strict_modes = 1;
  	if (options->keepalives == -1)
  		options->keepalives = 1;
+ 	if (options->watchdog_timeout == -1)
+ 		options->watchdog_timeout = 0;	/* 0 means "no timeout" */
  	if (options->log_facility == (SyslogFacility) (-1))
  		options->log_facility = SYSLOG_FACILITY_AUTH;
  	if (options->log_level == (LogLevel) (-1))
***************
*** 214,220 ****
  	sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
  	sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
  	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
! 	sBanner, sReverseMappingCheck
  } ServerOpCodes;
  
  /* Textual representation of the tokens. */
--- 217,223 ----
  	sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
  	sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile,
  	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
! 	sBanner, sReverseMappingCheck, sWatchdogTimeout
  } ServerOpCodes;
  
  /* Textual representation of the tokens. */
***************
*** 262,267 ****
--- 265,271 ----
  	{ "permitemptypasswords", sEmptyPasswd },
  	{ "uselogin", sUseLogin },
  	{ "keepalive", sKeepAlives },
+ 	{ "watchdogtimeout", sWatchdogTimeout },
  	{ "allowtcpforwarding", sAllowTcpForwarding },
  	{ "allowusers", sAllowUsers },
  	{ "denyusers", sDenyUsers },
***************
*** 570,575 ****
--- 574,583 ----
  		case sKeepAlives:
  			intptr = &options->keepalives;
  			goto parse_flag;
+ 
+ 		case sWatchdogTimeout:
+ 			intptr = &options->watchdog_timeout;
+ 			goto parse_int;
  
  		case sEmptyPasswd:
  			intptr = &options->permit_empty_passwd;
*** servconf.h.orig	Thu Feb 15 12:08:27 2001
--- servconf.h	Sat Apr  7 23:51:45 2001
***************
*** 58,63 ****
--- 58,66 ----
  	char   *xauth_location;	/* Location of xauth program */
  	int     strict_modes;	/* If true, require string home dir modes. */
  	int     keepalives;	/* If true, set SO_KEEPALIVE. */
+ 	int	watchdog_timeout;	/* Timeout of the watchdog timer which
+ 					   checks the input activities over
+ 					   encrypted channel. (in secs.) */
  	char   *ciphers;	/* Supported SSH2 ciphers. */
  	char   *macs;		/* Supported SSH2 macs. */
  	int	protocol;	/* Supported protocol versions. */
