Просмотр исходного кода

Add tunnel establish timeout

* Configurable timeout terminates controller if zero tunnels established within time limit.

* Move configuration-related constants to config.go. Eventually, more of these could be parameters.
Rod Hynes 11 лет назад
Родитель
Сommit
dc86764473
3 измененных файлов с 82 добавлено и 57 удалено
  1. 43 2
      psiphon/config.go
  2. 39 3
      psiphon/controller.go
  3. 0 52
      psiphon/defaults.go

+ 43 - 2
psiphon/config.go

@@ -23,8 +23,43 @@ import (
 	"encoding/json"
 	"errors"
 	"os"
+	"time"
 )
 
+// TODO: allow all params to be configured
+
+const (
+	VERSION                                      = "0.0.6"
+	DATA_STORE_FILENAME                          = "psiphon.db"
+	CONNECTION_WORKER_POOL_SIZE                  = 10
+	TUNNEL_POOL_SIZE                             = 1
+	TUNNEL_CONNECT_TIMEOUT                       = 15 * time.Second
+	TUNNEL_READ_TIMEOUT                          = 0 * time.Second
+	TUNNEL_WRITE_TIMEOUT                         = 5 * time.Second
+	TUNNEL_SSH_KEEP_ALIVE_PAYLOAD_MAX_BYTES      = 256
+	TUNNEL_SSH_KEEP_ALIVE_PERIOD_MIN             = 60 * time.Second
+	TUNNEL_SSH_KEEP_ALIVE_PERIOD_MAX             = 120 * time.Second
+	ESTABLISH_TUNNEL_TIMEOUT_SECONDS             = 300
+	ESTABLISH_TUNNEL_PAUSE_PERIOD                = 10 * time.Second
+	PORT_FORWARD_FAILURE_THRESHOLD               = 10
+	HTTP_PROXY_ORIGIN_SERVER_TIMEOUT             = 15 * time.Second
+	HTTP_PROXY_MAX_IDLE_CONNECTIONS_PER_HOST     = 50
+	FETCH_REMOTE_SERVER_LIST_TIMEOUT             = 10 * time.Second
+	FETCH_REMOTE_SERVER_LIST_RETRY_PERIOD        = 5 * time.Second
+	FETCH_REMOTE_SERVER_LIST_STALE_PERIOD        = 6 * time.Hour
+	PSIPHON_API_CLIENT_SESSION_ID_LENGTH         = 16
+	PSIPHON_API_SERVER_TIMEOUT                   = 20 * time.Second
+	PSIPHON_API_STATUS_REQUEST_PERIOD_MIN        = 5 * time.Minute
+	PSIPHON_API_STATUS_REQUEST_PERIOD_MAX        = 10 * time.Minute
+	PSIPHON_API_STATUS_REQUEST_PADDING_MAX_BYTES = 256
+	PSIPHON_API_CONNECTED_REQUEST_PERIOD         = 24 * time.Hour
+	PSIPHON_API_CONNECTED_REQUEST_RETRY_PERIOD   = 5 * time.Second
+)
+
+// To distinguish omitted timeout params from explicit 0 value timeout
+// params, these params are int pointers. nil means no param was supplied
+// so use the default; a non-nil pointer to 0 means no timeout.
+
 type Config struct {
 	LogFilename                        string
 	DataStoreDirectory                 string
@@ -38,6 +73,7 @@ type Config struct {
 	TunnelWholeDevice                  int
 	EgressRegion                       string
 	TunnelProtocol                     string
+	EstablishTunnelTimeoutSeconds      *int
 	LocalSocksProxyPort                int
 	LocalHttpProxyPort                 int
 	ConnectionWorkerPoolSize           int
@@ -85,6 +121,10 @@ func LoadConfig(configJson []byte) (*Config, error) {
 		}
 	}
 
+	if config.ClientVersion == "" {
+		config.ClientVersion = "0"
+	}
+
 	if config.TunnelProtocol != "" {
 		if !Contains(SupportedTunnelProtocols, config.TunnelProtocol) {
 			return nil, ContextError(
@@ -92,8 +132,9 @@ func LoadConfig(configJson []byte) (*Config, error) {
 		}
 	}
 
-	if config.ClientVersion == "" {
-		config.ClientVersion = "0"
+	if config.EstablishTunnelTimeoutSeconds == nil {
+		defaultEstablishTunnelTimeoutSeconds := ESTABLISH_TUNNEL_TIMEOUT_SECONDS
+		config.EstablishTunnelTimeoutSeconds = &defaultEstablishTunnelTimeoutSeconds
 	}
 
 	if config.ConnectionWorkerPoolSize == 0 {

+ 39 - 3
psiphon/controller.go

@@ -42,6 +42,7 @@ type Controller struct {
 	establishedTunnels        chan *Tunnel
 	failedTunnels             chan *Tunnel
 	tunnelMutex               sync.Mutex
+	establishedOnce           bool
 	tunnels                   []*Tunnel
 	nextTunnel                int
 	startedConnectedReporter  bool
@@ -77,6 +78,7 @@ func NewController(config *Config) (controller *Controller, err error) {
 		establishedTunnels:       make(chan *Tunnel, config.TunnelPoolSize),
 		failedTunnels:            make(chan *Tunnel, config.TunnelPoolSize),
 		tunnels:                  make([]*Tunnel, 0),
+		establishedOnce:          false,
 		startedConnectedReporter: false,
 		isEstablishing:           false,
 		establishPendingConns:    new(Conns),
@@ -112,9 +114,6 @@ func (controller *Controller) Run(shutdownBroadcast <-chan struct{}) {
 	}
 	defer httpProxy.Close()
 
-	// Note: unlike legacy Psiphon clients, this code always makes the
-	// fetch remote server list request
-
 	if !controller.config.DisableRemoteServerListFetcher {
 		controller.runWaitGroup.Add(1)
 		go controller.remoteServerListFetcher()
@@ -126,6 +125,11 @@ func (controller *Controller) Run(shutdownBroadcast <-chan struct{}) {
 	controller.runWaitGroup.Add(1)
 	go controller.runTunnels()
 
+	if *controller.config.EstablishTunnelTimeoutSeconds != 0 {
+		controller.runWaitGroup.Add(1)
+		go controller.establishTunnelWatcher()
+	}
+
 	// Wait while running
 
 	select {
@@ -182,6 +186,28 @@ loop:
 	NoticeInfo("exiting remote server list fetcher")
 }
 
+// establishTunnelWatcher terminates the controller if a tunnel
+// has not been established in the configured time period. This
+// is regardless of how many tunnels are presently active -- meaning
+// that if an active tunnel was established and lost the controller
+// is left running (to re-establish).
+func (controller *Controller) establishTunnelWatcher() {
+	defer controller.runWaitGroup.Done()
+
+	timeout := time.After(
+		time.Duration(*controller.config.EstablishTunnelTimeoutSeconds) * time.Second)
+	select {
+	case <-timeout:
+		if !controller.hasEstablishedOnce() {
+			NoticeAlert("failed to establish tunnel before timeout")
+			controller.SignalComponentFailure()
+		}
+	case <-controller.shutdownBroadcast:
+	}
+
+	NoticeInfo("exiting establish tunnel watcher")
+}
+
 // connectedReporter sends periodic "connected" requests to the Psiphon API.
 // These requests are for server-side unique user stats calculation. See the
 // comment in DoConnectedRequest for a description of the request mechanism.
@@ -350,11 +376,21 @@ func (controller *Controller) registerTunnel(tunnel *Tunnel) bool {
 			return false
 		}
 	}
+	controller.establishedOnce = true
 	controller.tunnels = append(controller.tunnels, tunnel)
 	NoticeTunnels(len(controller.tunnels))
 	return true
 }
 
+// hasEstablishedOnce indicates if at least one active tunnel has
+// been established up to this point. This is regardeless of how many
+// tunnels are presently active.
+func (controller *Controller) hasEstablishedOnce() bool {
+	controller.tunnelMutex.Lock()
+	defer controller.tunnelMutex.Unlock()
+	return controller.establishedOnce
+}
+
 // isFullyEstablished indicates if the pool of active tunnels is full.
 func (controller *Controller) isFullyEstablished() bool {
 	controller.tunnelMutex.Lock()

+ 0 - 52
psiphon/defaults.go

@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2015, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package psiphon
-
-import (
-	"time"
-)
-
-const (
-	VERSION                                      = "0.0.6"
-	DATA_STORE_FILENAME                          = "psiphon.db"
-	CONNECTION_WORKER_POOL_SIZE                  = 10
-	TUNNEL_POOL_SIZE                             = 1
-	TUNNEL_CONNECT_TIMEOUT                       = 15 * time.Second
-	TUNNEL_READ_TIMEOUT                          = 0 * time.Second
-	TUNNEL_WRITE_TIMEOUT                         = 5 * time.Second
-	TUNNEL_SSH_KEEP_ALIVE_PAYLOAD_MAX_BYTES      = 256
-	TUNNEL_SSH_KEEP_ALIVE_PERIOD_MIN             = 60 * time.Second
-	TUNNEL_SSH_KEEP_ALIVE_PERIOD_MAX             = 120 * time.Second
-	ESTABLISH_TUNNEL_TIMEOUT                     = 60 * time.Second
-	ESTABLISH_TUNNEL_PAUSE_PERIOD                = 10 * time.Second
-	PORT_FORWARD_FAILURE_THRESHOLD               = 10
-	HTTP_PROXY_ORIGIN_SERVER_TIMEOUT             = 15 * time.Second
-	HTTP_PROXY_MAX_IDLE_CONNECTIONS_PER_HOST     = 50
-	FETCH_REMOTE_SERVER_LIST_TIMEOUT             = 10 * time.Second
-	FETCH_REMOTE_SERVER_LIST_RETRY_PERIOD        = 5 * time.Second
-	FETCH_REMOTE_SERVER_LIST_STALE_PERIOD        = 6 * time.Hour
-	PSIPHON_API_CLIENT_SESSION_ID_LENGTH         = 16
-	PSIPHON_API_SERVER_TIMEOUT                   = 20 * time.Second
-	PSIPHON_API_STATUS_REQUEST_PERIOD_MIN        = 5 * time.Minute
-	PSIPHON_API_STATUS_REQUEST_PERIOD_MAX        = 10 * time.Minute
-	PSIPHON_API_STATUS_REQUEST_PADDING_MAX_BYTES = 256
-	PSIPHON_API_CONNECTED_REQUEST_PERIOD         = 24 * time.Hour
-	PSIPHON_API_CONNECTED_REQUEST_RETRY_PERIOD   = 5 * time.Second
-)