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

Merge remote-tracking branch 'upstream/master'

Adam Pritchard 11 лет назад
Родитель
Сommit
228477b993
6 измененных файлов с 63 добавлено и 28 удалено
  1. 23 13
      psiphon/httpProxy.go
  2. 15 4
      psiphon/meekConn.go
  3. 1 0
      psiphon/obfuscatedSshConn.go
  4. 2 1
      psiphon/obfuscator.go
  5. 21 9
      psiphon/socksProxy.go
  6. 1 1
      psiphon/utils.go

+ 23 - 13
psiphon/httpProxy.go

@@ -31,11 +31,12 @@ import (
 // HttpProxy is a HTTP server that relays HTTP requests through
 // the tunnel SSH client.
 type HttpProxy struct {
-	tunneler       Tunneler
-	listener       net.Listener
-	serveWaitGroup *sync.WaitGroup
-	httpRelay      *http.Transport
-	openConns      *Conns
+	tunneler               Tunneler
+	listener               net.Listener
+	serveWaitGroup         *sync.WaitGroup
+	httpRelay              *http.Transport
+	openConns              *Conns
+	stopListeningBroadcast chan struct{}
 }
 
 // NewHttpProxy initializes and runs a new HTTP proxy server.
@@ -56,11 +57,12 @@ func NewHttpProxy(config *Config, tunneler Tunneler) (proxy *HttpProxy, err erro
 		ResponseHeaderTimeout: HTTP_PROXY_ORIGIN_SERVER_TIMEOUT,
 	}
 	proxy = &HttpProxy{
-		tunneler:       tunneler,
-		listener:       listener,
-		serveWaitGroup: new(sync.WaitGroup),
-		httpRelay:      transport,
-		openConns:      new(Conns),
+		tunneler:               tunneler,
+		listener:               listener,
+		serveWaitGroup:         new(sync.WaitGroup),
+		httpRelay:              transport,
+		openConns:              new(Conns),
+		stopListeningBroadcast: make(chan struct{}),
 	}
 	proxy.serveWaitGroup.Add(1)
 	go proxy.serve()
@@ -70,6 +72,7 @@ func NewHttpProxy(config *Config, tunneler Tunneler) (proxy *HttpProxy, err erro
 
 // Close terminates the HTTP server.
 func (proxy *HttpProxy) Close() {
+	close(proxy.stopListeningBroadcast)
 	proxy.listener.Close()
 	proxy.serveWaitGroup.Wait()
 	// Close local->proxy persistent connections
@@ -226,9 +229,16 @@ func (proxy *HttpProxy) serve() {
 	}
 	// Note: will be interrupted by listener.Close() call made by proxy.Close()
 	err := httpServer.Serve(proxy.listener)
-	if err != nil {
-		proxy.tunneler.SignalFailure()
-		Notice(NOTICE_ALERT, "%s", ContextError(err))
+	// Can't check for the exact error that Close() will cause in Accept(),
+	// (see: https://code.google.com/p/go/issues/detail?id=4373). So using an
+	// explicit stop signal to stop gracefully.
+	select {
+	case <-proxy.stopListeningBroadcast:
+	default:
+		if err != nil {
+			proxy.tunneler.SignalFailure()
+			Notice(NOTICE_ALERT, "%s", ContextError(err))
+		}
 	}
 	Notice(NOTICE_HTTP_PROXY, "HTTP proxy stopped")
 }

+ 15 - 4
psiphon/meekConn.go

@@ -44,7 +44,7 @@ import (
 // https://bitbucket.org/psiphon/psiphon-circumvention-system/src/default/go/meek-client/meek-client.go
 
 const (
-	MEEK_PROTOCOL_VERSION      = 1
+	MEEK_PROTOCOL_VERSION      = 2
 	MEEK_COOKIE_MAX_PADDING    = 32
 	MAX_SEND_PAYLOAD_LENGTH    = 65536
 	FULL_RECEIVE_BUFFER_LENGTH = 4194304
@@ -435,6 +435,14 @@ func (meek *MeekConn) roundTrip(sendPayload []byte) (receivedPayload io.ReadClos
 	if response.StatusCode != http.StatusOK {
 		return nil, ContextError(fmt.Errorf("http request failed %d", response.StatusCode))
 	}
+        // observe response cookies for meek session key token.
+        // Once found it must be used for all consecutive requests made to the server
+        for _, c := range response.Cookies() {
+            if meek.cookie.Name == c.Name {
+                meek.cookie.Value = c.Value
+                break
+            }
+        }
 	return response.Body, nil
 }
 
@@ -444,14 +452,16 @@ type meekCookieData struct {
 	MeekProtocolVersion int    `json:"v"`
 }
 
-// makeCookie creates the cookie to be sent with all meek HTTP requests.
+// makeCookie creates the cookie to be sent with initial meek HTTP request.
 // The purpose of the cookie is to send the following to the server:
 //   ServerAddress -- the Psiphon Server address the meek server should relay to
 //   SessionID -- the Psiphon session ID (used by meek server to relay geolocation
 //     information obtained from the CDN through to the Psiphon Server)
 //   MeekProtocolVersion -- tells the meek server that this client understands
 //     the latest protocol.
-// The entire cookie also acts as an meek/HTTP session ID.
+// The server will create a session using these values and send the session ID
+// back to the client via Set-Cookie header. Client must use that value with
+// all consequent HTTP requests
 // In unfronted meek mode, the cookie is visible over the adversary network, so the
 // cookie is encrypted and obfuscated.
 func makeCookie(serverEntry *ServerEntry, sessionId string) (cookie *http.Cookie, err error) {
@@ -506,7 +516,8 @@ func makeCookie(serverEntry *ServerEntry, sessionId string) (cookie *http.Cookie
 	// The format is <random letter 'A'-'Z'>=<base64 data>, which is intended to match common cookie formats.
 	A := int('A')
 	Z := int('Z')
-	letterIndex, err := MakeSecureRandomInt(Z - A)
+	// letterIndex is integer in range [int('A'), int('Z')]
+	letterIndex, err := MakeSecureRandomInt(Z - A + 1)
 	if err != nil {
 		return nil, ContextError(err)
 	}

+ 1 - 0
psiphon/obfuscatedSshConn.go

@@ -304,6 +304,7 @@ func (conn *ObfuscatedSshConn) transformAndWrite(buffer []byte) (err error) {
 			// See RFC 4253 sec. 6 for constraints
 			possiblePaddings := (SSH_MAX_PADDING_LENGTH - paddingLength) / SSH_PADDING_MULTIPLE
 			if possiblePaddings > 0 {
+				// selectedPadding is integer in range [0, possiblePaddings)
 				selectedPadding, err := MakeSecureRandomInt(possiblePaddings)
 				if err != nil {
 					return ContextError(err)

+ 2 - 1
psiphon/obfuscator.go

@@ -125,7 +125,8 @@ func deriveKey(seed, keyword, iv []byte) ([]byte, error) {
 }
 
 func makeSeedMessage(maxPadding int, seed []byte, clientToServerCipher *rc4.Cipher) ([]byte, error) {
-	paddingLength, err := MakeSecureRandomInt(maxPadding)
+	// paddingLength is integer in range [0, maxPadding]
+	paddingLength, err := MakeSecureRandomInt(maxPadding + 1)
 	if err != nil {
 		return nil, ContextError(err)
 	}

+ 21 - 9
psiphon/socksProxy.go

@@ -31,10 +31,11 @@ import (
 // the tunnel SSH client and relays traffic through the port
 // forward.
 type SocksProxy struct {
-	tunneler       Tunneler
-	listener       *socks.SocksListener
-	serveWaitGroup *sync.WaitGroup
-	openConns      *Conns
+	tunneler               Tunneler
+	listener               *socks.SocksListener
+	serveWaitGroup         *sync.WaitGroup
+	openConns              *Conns
+	stopListeningBroadcast chan struct{}
 }
 
 // NewSocksProxy initializes a new SOCKS server. It begins listening for
@@ -47,10 +48,11 @@ func NewSocksProxy(config *Config, tunneler Tunneler) (proxy *SocksProxy, err er
 		return nil, ContextError(err)
 	}
 	proxy = &SocksProxy{
-		tunneler:       tunneler,
-		listener:       listener,
-		serveWaitGroup: new(sync.WaitGroup),
-		openConns:      new(Conns),
+		tunneler:               tunneler,
+		listener:               listener,
+		serveWaitGroup:         new(sync.WaitGroup),
+		openConns:              new(Conns),
+		stopListeningBroadcast: make(chan struct{}),
 	}
 	proxy.serveWaitGroup.Add(1)
 	go proxy.serve()
@@ -61,6 +63,7 @@ func NewSocksProxy(config *Config, tunneler Tunneler) (proxy *SocksProxy, err er
 // Close terminates the listener and waits for the accept loop
 // goroutine to complete.
 func (proxy *SocksProxy) Close() {
+	close(proxy.stopListeningBroadcast)
 	proxy.listener.Close()
 	proxy.serveWaitGroup.Wait()
 	proxy.openConns.CloseAll()
@@ -86,15 +89,24 @@ func (proxy *SocksProxy) socksConnectionHandler(localConn *socks.SocksConn) (err
 func (proxy *SocksProxy) serve() {
 	defer proxy.listener.Close()
 	defer proxy.serveWaitGroup.Done()
+loop:
 	for {
 		// Note: will be interrupted by listener.Close() call made by proxy.Close()
 		socksConnection, err := proxy.listener.AcceptSocks()
+		// Can't check for the exact error that Close() will cause in Accept(),
+		// (see: https://code.google.com/p/go/issues/detail?id=4373). So using an
+		// explicit stop signal to stop gracefully.
+		select {
+		case <-proxy.stopListeningBroadcast:
+			break loop
+		default:
+		}
 		if err != nil {
 			Notice(NOTICE_ALERT, "SOCKS proxy accept error: %s", err)
 			if e, ok := err.(net.Error); ok && !e.Temporary() {
 				proxy.tunneler.SignalFailure()
 				// Fatal error, stop the proxy
-				break
+				break loop
 			}
 			// Temporary error, keep running
 			continue

+ 1 - 1
psiphon/utils.go

@@ -42,7 +42,7 @@ func Contains(list []string, target string) bool {
 }
 
 // MakeSecureRandomInt is a helper function that wraps
-// crypto/rand.Int.
+// crypto/rand.Int, which returns a uniform random value in [0, max).
 func MakeSecureRandomInt(max int) (int, error) {
 	randomInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
 	if err != nil {