Browse Source

upstreamproxy.Error type, minor fixes

Eugene Fryntov 10 years ago
parent
commit
a6f8a4136d

+ 2 - 2
psiphon/upstreamproxy/auth_basic.go

@@ -21,7 +21,7 @@ package upstreamproxy
 
 import (
 	"encoding/base64"
-	"errors"
+	"fmt"
 	"net/http"
 )
 
@@ -47,6 +47,6 @@ func (a *BasicHttpAuthenticator) Authenticate(req *http.Request, resp *http.Resp
 		a.state = BASIC_HTTP_AUTH_STATE_RESPONSE_GENERATED
 		return nil
 	} else {
-		return errors.New("upstreamproxy: Authorization is not accepted by the proxy server")
+		return proxyError(fmt.Errorf("Authorization is not accepted by the proxy server"))
 	}
 }

+ 4 - 4
psiphon/upstreamproxy/auth_digest.go

@@ -23,7 +23,6 @@ import (
 	"crypto/md5"
 	"crypto/rand"
 	"encoding/base64"
-	"errors"
 	"fmt"
 	"net/http"
 	"strings"
@@ -127,15 +126,16 @@ func h(data string) string {
 
 func (a *DigestHttpAuthenticator) Authenticate(req *http.Request, resp *http.Response, username, password string) error {
 	if a.state != DIGEST_HTTP_AUTH_STATE_CHALLENGE_RECEIVED {
-		return errors.New("upstreamproxy: Authorization is not accepted by the proxy server")
+		return proxyError(fmt.Errorf("Authorization is not accepted by the proxy server"))
 	}
 	challenges, err := parseAuthChallenge(resp)
 	if err != nil {
+		//already wrapped in proxyError
 		return err
 	}
 	challenge := challenges["Digest"]
 	if len(challenge) == 0 {
-		return errors.New("upstreamproxy: Digest authentication challenge is empty")
+		return proxyError(fmt.Errorf("Digest authentication challenge is empty"))
 	}
 	//parse challenge
 	digestParams := map[string]string{}
@@ -147,7 +147,7 @@ func (a *DigestHttpAuthenticator) Authenticate(req *http.Request, resp *http.Res
 		digestParams[strings.Trim(param[0], "\" ")] = strings.Trim(param[1], "\" ")
 	}
 	if len(digestParams) == 0 {
-		return errors.New("upstreamproxy: Digest authentication challenge is malformed")
+		return proxyError(fmt.Errorf("Digest authentication challenge is malformed"))
 	}
 
 	algorithm := digestParams["algorithm"]

+ 9 - 9
psiphon/upstreamproxy/auth_ntlm.go

@@ -21,7 +21,7 @@ package upstreamproxy
 
 import (
 	"encoding/base64"
-	"errors"
+	"fmt"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/upstreamproxy/go-ntlm/ntlm"
 	"net/http"
 	"strings"
@@ -45,7 +45,7 @@ func newNTLMAuthenticator() *NTLMHttpAuthenticator {
 
 func (a *NTLMHttpAuthenticator) Authenticate(req *http.Request, resp *http.Response, username, password string) error {
 	if a.state == NTLM_HTTP_AUTH_STATE_RESPONSE_TYPE3_GENERATED {
-		return errors.New("upstreamproxy: Authorization is not accepted by the proxy server")
+		return proxyError(fmt.Errorf("Authorization is not accepted by the proxy server"))
 	}
 	challenges, err := parseAuthChallenge(resp)
 
@@ -56,20 +56,20 @@ func (a *NTLMHttpAuthenticator) Authenticate(req *http.Request, resp *http.Respo
 		a.state = NTLM_HTTP_AUTH_STATE_RESPONSE_TYPE1_GENERATED
 	}
 	if !ok {
-		return errors.New("upstreamproxy: Bad proxy response, no NTLM challenge for NTLMHttpAuthenticator")
+		return proxyError(fmt.Errorf("Bad proxy response, no NTLM challenge for NTLMHttpAuthenticator"))
 	}
 
 	var ntlmMsg []byte
 
 	session, err := ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionOrientedMode)
 	if err != nil {
-		return err
+		return proxyError(err)
 	}
 	if a.state == NTLM_HTTP_AUTH_STATE_CHALLENGE_RECEIVED {
 		//generate TYPE 1 message
 		negotiate, err := session.GenerateNegotiateMessage()
 		if err != nil {
-			return err
+			return proxyError(err)
 		}
 		ntlmMsg = negotiate.Bytes()
 		a.state = NTLM_HTTP_AUTH_STATE_RESPONSE_TYPE1_GENERATED
@@ -88,17 +88,17 @@ func (a *NTLMHttpAuthenticator) Authenticate(req *http.Request, resp *http.Respo
 		}
 		challengeBytes, err := base64.StdEncoding.DecodeString(challenge)
 		if err != nil {
-			return err
+			return proxyError(fmt.Errorf("NTLM challeenge base 64 decoding: %v", err))
 		}
 		session.SetUserInfo(NTUser, password, NTDomain)
 		ntlmChallenge, err := ntlm.ParseChallengeMessage(challengeBytes)
 		if err != nil {
-			return err
+			return proxyError(err)
 		}
 		session.ProcessChallengeMessage(ntlmChallenge)
 		authenticate, err := session.GenerateAuthenticateMessage()
 		if err != nil {
-			return err
+			return proxyError(err)
 		}
 		ntlmMsg = authenticate.Bytes()
 		a.state = NTLM_HTTP_AUTH_STATE_RESPONSE_TYPE3_GENERATED
@@ -106,5 +106,5 @@ func (a *NTLMHttpAuthenticator) Authenticate(req *http.Request, resp *http.Respo
 		return nil
 	}
 
-	return errors.New("upstreamproxy: Authorization is not accepted by the proxy server")
+	return proxyError(fmt.Errorf("Authorization is not accepted by the proxy server"))
 }

+ 3 - 2
psiphon/upstreamproxy/http_authenticator.go

@@ -52,7 +52,7 @@ func parseAuthChallenge(resp *http.Response) (map[string]string, error) {
 		}
 	}
 	if len(challenges) == 0 {
-		return nil, fmt.Errorf("upstreamproxy: No valid challenges in the Proxy-Authenticate header")
+		return nil, proxyError(fmt.Errorf("No valid challenges in the Proxy-Authenticate header"))
 	}
 	return challenges, nil
 }
@@ -61,6 +61,7 @@ func NewHttpAuthenticator(resp *http.Response) (HttpAuthenticator, error) {
 
 	challenges, err := parseAuthChallenge(resp)
 	if err != nil {
+		//Already wrapped in proxyError
 		return nil, err
 	}
 
@@ -78,5 +79,5 @@ func NewHttpAuthenticator(resp *http.Response) (HttpAuthenticator, error) {
 	for scheme := range challenges {
 		schemes = append(schemes, scheme)
 	}
-	return nil, fmt.Errorf("Unsupported proxy authentication scheme in %v", schemes)
+	return nil, proxyError(fmt.Errorf("Unsupported proxy authentication scheme in %v", schemes))
 }

+ 36 - 29
psiphon/upstreamproxy/proxy_http.go

@@ -47,7 +47,6 @@ package upstreamproxy
 
 import (
 	"bufio"
-	"errors"
 	"fmt"
 	"golang.org/x/net/proxy"
 	"net"
@@ -79,44 +78,40 @@ func newHTTP(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
 
 func (hp *httpProxy) Dial(network, addr string) (net.Conn, error) {
 	// Dial and create the http client connection.
-	pc := &proxyConn{authState: HTTP_AUTH_STATE_UNCHALLENGED}
-	err := pc.makeNewClientConn(hp.forward, hp.hostPort)
+	pc := &proxyConn{
+		authState: HTTP_AUTH_STATE_UNCHALLENGED,
+		dialFn:    hp.forward.Dial,
+		proxyAddr: hp.hostPort,
+	}
+	err := pc.makeNewClientConn()
 	if err != nil {
-		return nil, fmt.Errorf("upstreamproxy: makeNewClientConn error: %v", err)
+		//Already wrapped in proxyError
+		return nil, err
 	}
 
 handshakeLoop:
 	for {
 		err := pc.handshake(addr, hp.username, hp.password)
-		if err != nil {
-			return nil, err
-		}
 		switch pc.authState {
 		case HTTP_AUTH_STATE_SUCCESS:
 			pc.hijackedConn, pc.staleReader = pc.httpClientConn.Hijack()
 			return pc, nil
 		case HTTP_AUTH_STATE_FAILURE:
+			//err already wrapped in proxyError
 			return nil, err
 		case HTTP_AUTH_STATE_CHALLENGED:
-			// the server may send Connection: close,
-			// at this point we just going to create a new
-			// ClientConn and continue the handshake
-			if err == httputil.ErrPersistEOF {
-				err = pc.makeNewClientConn(hp.forward, hp.hostPort)
-				if err != nil {
-					return nil, fmt.Errorf("upstreamproxy: makeNewClientConn error: %v", err)
-				}
-			}
 			continue
 		default:
 			break handshakeLoop
 		}
 	}
-	return nil, fmt.Errorf("Unknown handshake error")
+	return nil, proxyError(fmt.Errorf("Unknown handshake error"))
 
 }
 
 type proxyConn struct {
+	dialFn         DialFunc
+	proxyAddr      string
 	httpClientConn *httputil.ClientConn
 	hijackedConn   net.Conn
 	staleReader    *bufio.Reader
@@ -132,7 +127,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 	if err != nil {
 		pc.httpClientConn.Close()
 		pc.authState = HTTP_AUTH_STATE_FAILURE
-		return err
+		return proxyError(fmt.Errorf("Failed to parse proxy address: %v", err))
 	}
 	reqURL.Scheme = ""
 
@@ -140,7 +135,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 	if err != nil {
 		pc.httpClientConn.Close()
 		pc.authState = HTTP_AUTH_STATE_FAILURE
-		return err
+		return proxyError(fmt.Errorf("Create proxy request: %v", err))
 	}
 	req.Close = false
 	req.Header.Set("User-Agent", "")
@@ -149,6 +144,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 		err := pc.authenticator.Authenticate(req, pc.authResponse, username, password)
 		if err != nil {
 			pc.authState = HTTP_AUTH_STATE_FAILURE
+			//Already wrapped in proxyError
 			return err
 		}
 	}
@@ -158,7 +154,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 	if err != nil && err != httputil.ErrPersistEOF {
 		pc.httpClientConn.Close()
 		pc.authState = HTTP_AUTH_STATE_FAILURE
-		return err
+		return proxyError(fmt.Errorf("making proxy request: %v", err))
 	}
 
 	if resp.StatusCode == 200 {
@@ -173,6 +169,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 			if auth_err != nil {
 				pc.httpClientConn.Close()
 				pc.authState = HTTP_AUTH_STATE_FAILURE
+				//Already wrapped in proxyError
 				return auth_err
 			}
 		}
@@ -182,21 +179,31 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 		if username == "" {
 			pc.httpClientConn.Close()
 			pc.authState = HTTP_AUTH_STATE_FAILURE
-			return errors.New("upstreamproxy: No credentials provided for proxy auth")
+			return proxyError(fmt.Errorf("No username credentials provided for proxy auth"))
 		}
-		return err
+		if err == httputil.ErrPersistEOF {
+			// the server may send Connection: close,
+			// at this point we just going to create a new
+			// ClientConn and continue the handshake
+			err = pc.makeNewClientConn()
+			if err != nil {
+				//Already wrapped in proxyError
+				return err
+			}
+		}
+		return nil
 	}
 	pc.authState = HTTP_AUTH_STATE_FAILURE
-	return err
+	return proxyError(err)
 }
 
-func (pc *proxyConn) makeNewClientConn(dialer proxy.Dialer, addr string) error {
-	c, err := dialer.Dial("tcp", addr)
+func (pc *proxyConn) makeNewClientConn() error {
+	c, err := pc.dialFn("tcp", pc.proxyAddr)
 	if pc.httpClientConn != nil {
 		pc.httpClientConn.Close()
 	}
 	if err != nil {
-		return err
+		return proxyError(fmt.Errorf("makeNewClientConn: %v", err))
 	}
 	pc.httpClientConn = httputil.NewClientConn(c, nil)
 	return nil
@@ -229,15 +236,15 @@ func (pc *proxyConn) RemoteAddr() net.Addr {
 }
 
 func (pc *proxyConn) SetDeadline(t time.Time) error {
-	return errors.New("not supported")
+	return proxyError(fmt.Errorf("not supported"))
 }
 
 func (pc *proxyConn) SetReadDeadline(t time.Time) error {
-	return errors.New("not supported")
+	return proxyError(fmt.Errorf("not supported"))
 }
 
 func (pc *proxyConn) SetWriteDeadline(t time.Time) error {
-	return errors.New("not supported")
+	return proxyError(fmt.Errorf("not supported"))
 }
 
 func init() {

+ 10 - 11
psiphon/upstreamproxy/proxy_socks4.go

@@ -52,7 +52,6 @@
 package upstreamproxy
 
 import (
-	"errors"
 	"fmt"
 	"io"
 	"net"
@@ -93,31 +92,31 @@ func newSOCKS4(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
 
 func (s *socks4Proxy) Dial(network, addr string) (net.Conn, error) {
 	if network != "tcp" && network != "tcp4" {
-		return nil, errors.New("invalid network type")
+		return nil, proxyError(fmt.Errorf("invalid network type"))
 	}
 
 	// Deal with the destination address/string.
 	ipStr, portStr, err := net.SplitHostPort(addr)
 	if err != nil {
-		return nil, err
+		return nil, proxyError(fmt.Errorf("parsing destination address: %v", err))
 	}
 	ip := net.ParseIP(ipStr)
 	if ip == nil {
-		return nil, errors.New("failed to parse destination IP")
+		return nil, proxyError(fmt.Errorf("failed to parse destination IP"))
 	}
 	ip4 := ip.To4()
 	if ip4 == nil {
-		return nil, errors.New("destination address is not IPv4")
+		return nil, proxyError(fmt.Errorf("destination address is not IPv4"))
 	}
 	port, err := strconv.ParseUint(portStr, 10, 16)
 	if err != nil {
-		return nil, err
+		return nil, proxyError(fmt.Errorf("failed to parse destination port: %v", err))
 	}
 
 	// Connect to the proxy.
 	c, err := s.forward.Dial("tcp", s.hostPort)
 	if err != nil {
-		return nil, err
+		return nil, proxyError(fmt.Errorf("failed to dial SOCKS4a proxy: %v", err))
 	}
 
 	// Make/write the request:
@@ -137,7 +136,7 @@ func (s *socks4Proxy) Dial(network, addr string) (net.Conn, error) {
 	_, err = c.Write(req)
 	if err != nil {
 		c.Close()
-		return nil, err
+		return nil, proxyError(fmt.Errorf("failed to write to SOCKS4a proxy: %v", err))
 	}
 
 	// Read the response:
@@ -149,15 +148,15 @@ func (s *socks4Proxy) Dial(network, addr string) (net.Conn, error) {
 	_, err = io.ReadFull(c, resp[:])
 	if err != nil {
 		c.Close()
-		return nil, err
+		return nil, proxyError(fmt.Errorf("failed to read SOCKS4a proxy response: %v", err))
 	}
 	if resp[0] != socks4ReplyVersion {
 		c.Close()
-		return nil, errors.New("proxy returned invalid SOCKS4 version")
+		return nil, proxyError(fmt.Errorf("proxy returned invalid SOCKS4 version"))
 	}
 	if resp[1] != socks4Granted {
 		c.Close()
-		return nil, fmt.Errorf("proxy error: %s", socks4ErrorToString(resp[1]))
+		return nil, proxyError(fmt.Errorf("proxy error: %s", socks4ErrorToString(resp[1])))
 	}
 
 	return c, nil

+ 1 - 2
psiphon/upstreamproxy/transport_proxy_auth.go

@@ -170,12 +170,11 @@ func (tc *transportConn) Read(p []byte) (int, error) {
 				tc.connReader = bufio.NewReader(tc.Conn)
 			}
 
+			// Authenticate and replay the request
 			err = tc.authenticator.Authenticate(tc.lastRequest, resp, tc.transport.Username, tc.transport.Password)
 			if err != nil {
 				return 0, err
 			}
-
-			//Replay authenticated request
 			tc.lastRequest.WriteProxy(tc)
 			return tc.Read(p)
 		}