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

New config option for adding custom HTTP headers to HTTP(s) proxied connections

Eugene Fryntov 9 лет назад
Родитель
Сommit
92822fce60

+ 1 - 0
psiphon/TCPConn.go

@@ -150,6 +150,7 @@ func proxiedTcpDial(
 		&upstreamproxy.UpstreamProxyConfig{
 		&upstreamproxy.UpstreamProxyConfig{
 			ForwardDialFunc: dialer,
 			ForwardDialFunc: dialer,
 			ProxyURIString:  config.UpstreamProxyUrl,
 			ProxyURIString:  config.UpstreamProxyUrl,
+			CustomHeaders:   config.UpstreamProxyCustomHeaders,
 		})
 		})
 	netConn, err := upstreamDialer("tcp", addr)
 	netConn, err := upstreamDialer("tcp", addr)
 	if _, ok := err.(*upstreamproxy.Error); ok {
 	if _, ok := err.(*upstreamproxy.Error); ok {

+ 6 - 0
psiphon/config.go

@@ -23,6 +23,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"net/http"
 	"os"
 	"os"
 	"strconv"
 	"strconv"
 	"time"
 	"time"
@@ -198,6 +199,11 @@ type Config struct {
 	// https://github.com/Psiphon-Labs/psiphon-tunnel-core/tree/master/psiphon/upstreamproxy
 	// https://github.com/Psiphon-Labs/psiphon-tunnel-core/tree/master/psiphon/upstreamproxy
 	UpstreamProxyUrl string
 	UpstreamProxyUrl string
 
 
+	// UpstreamProxyCustomHeaders is a set of additional arbitrary HTTP headers that are
+	// added to all requests made through the upstream proxy specified by UpstreamProxyUrl
+	// NOTE: Only HTTP(s) proxies use this if specified
+	UpstreamProxyCustomHeaders http.Header
+
 	// NetworkConnectivityChecker is an interface that enables the core tunnel to call
 	// NetworkConnectivityChecker is an interface that enables the core tunnel to call
 	// into the host application to check for network connectivity. This parameter is
 	// into the host application to check for network connectivity. This parameter is
 	// only applicable to library deployments.
 	// only applicable to library deployments.

+ 1 - 0
psiphon/controller.go

@@ -95,6 +95,7 @@ func NewController(config *Config) (controller *Controller, err error) {
 	untunneledPendingConns := new(Conns)
 	untunneledPendingConns := new(Conns)
 	untunneledDialConfig := &DialConfig{
 	untunneledDialConfig := &DialConfig{
 		UpstreamProxyUrl:              config.UpstreamProxyUrl,
 		UpstreamProxyUrl:              config.UpstreamProxyUrl,
+		UpstreamProxyCustomHeaders:    config.UpstreamProxyCustomHeaders,
 		PendingConns:                  untunneledPendingConns,
 		PendingConns:                  untunneledPendingConns,
 		DeviceBinder:                  config.DeviceBinder,
 		DeviceBinder:                  config.DeviceBinder,
 		DnsServerGetter:               config.DnsServerGetter,
 		DnsServerGetter:               config.DnsServerGetter,

+ 1 - 1
psiphon/meekConn.go

@@ -236,7 +236,7 @@ func DialMeek(
 		}
 		}
 		if proxyUrl != nil {
 		if proxyUrl != nil {
 			// Wrap transport with a transport that can perform HTTP proxy auth negotiation
 			// Wrap transport with a transport that can perform HTTP proxy auth negotiation
-			transport, err = upstreamproxy.NewProxyAuthTransport(httpTransport)
+			transport, err = upstreamproxy.NewProxyAuthTransport(httpTransport, meekDialConfig.UpstreamProxyCustomHeaders)
 			if err != nil {
 			if err != nil {
 				return nil, ContextError(err)
 				return nil, ContextError(err)
 			}
 			}

+ 4 - 0
psiphon/net.go

@@ -88,6 +88,10 @@ type DialConfig struct {
 	// supported, those protocols will not connect.
 	// supported, those protocols will not connect.
 	UpstreamProxyUrl string
 	UpstreamProxyUrl string
 
 
+	// UpstreamProxyCustomHeader is a set of additional arbitrary HTTP headers that are
+	// added to CONNECT requests made through the upstream proxy specified by UpstreamProxyUrl
+	UpstreamProxyCustomHeaders http.Header
+
 	ConnectTimeout time.Duration
 	ConnectTimeout time.Duration
 
 
 	// PendingConns is used to track and interrupt dials in progress.
 	// PendingConns is used to track and interrupt dials in progress.

+ 2 - 1
psiphon/upstreamproxy/README.md

@@ -24,8 +24,9 @@ Note: `NewProxyDialFunc` returns `ForwardDialFunc` if `ProxyURIString` is empty
 func doAuthenticatedHTTP() {
 func doAuthenticatedHTTP() {
 	proxyUrl, err := url.Parse("http://user:password@172.16.1.1:8080")
 	proxyUrl, err := url.Parse("http://user:password@172.16.1.1:8080")
 	transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
 	transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
+	customHeader := http.Header{"Test" :{"test"}}
 
 
-	authHttpTransport, err := upstreamproxy.NewProxyAuthTransport(transport)
+	authHttpTransport, err := upstreamproxy.NewProxyAuthTransport(transport, customHeader)
 	if err != nil {
 	if err != nil {
 		fmt.Println("Error: ", err)
 		fmt.Println("Error: ", err)
 		return
 		return

+ 20 - 8
psiphon/upstreamproxy/proxy_http.go

@@ -48,20 +48,22 @@ package upstreamproxy
 import (
 import (
 	"bufio"
 	"bufio"
 	"fmt"
 	"fmt"
-	"golang.org/x/net/proxy"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"net/http/httputil"
 	"net/http/httputil"
 	"net/url"
 	"net/url"
 	"time"
 	"time"
+
+	"golang.org/x/net/proxy"
 )
 )
 
 
 // httpProxy is a HTTP connect proxy.
 // httpProxy is a HTTP connect proxy.
 type httpProxy struct {
 type httpProxy struct {
-	hostPort string
-	username string
-	password string
-	forward  proxy.Dialer
+	hostPort      string
+	username      string
+	password      string
+	forward       proxy.Dialer
+	customHeaders http.Header
 }
 }
 
 
 func newHTTP(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
 func newHTTP(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
@@ -73,15 +75,20 @@ func newHTTP(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
 		hp.password, _ = uri.User.Password()
 		hp.password, _ = uri.User.Password()
 	}
 	}
 
 
+	if upstreamProyConfig, ok := forward.(*UpstreamProxyConfig); ok {
+		hp.customHeaders = upstreamProyConfig.CustomHeaders
+	}
+
 	return hp, nil
 	return hp, nil
 }
 }
 
 
 func (hp *httpProxy) Dial(network, addr string) (net.Conn, error) {
 func (hp *httpProxy) Dial(network, addr string) (net.Conn, error) {
 	// Dial and create the http client connection.
 	// Dial and create the http client connection.
 	pc := &proxyConn{
 	pc := &proxyConn{
-		authState: HTTP_AUTH_STATE_UNCHALLENGED,
-		dialFn:    hp.forward.Dial,
-		proxyAddr: hp.hostPort,
+		authState:     HTTP_AUTH_STATE_UNCHALLENGED,
+		dialFn:        hp.forward.Dial,
+		proxyAddr:     hp.hostPort,
+		customHeaders: hp.customHeaders,
 	}
 	}
 	err := pc.makeNewClientConn()
 	err := pc.makeNewClientConn()
 	if err != nil {
 	if err != nil {
@@ -116,6 +123,7 @@ handshakeLoop:
 type proxyConn struct {
 type proxyConn struct {
 	dialFn         DialFunc
 	dialFn         DialFunc
 	proxyAddr      string
 	proxyAddr      string
+	customHeaders  http.Header
 	httpClientConn *httputil.ClientConn
 	httpClientConn *httputil.ClientConn
 	hijackedConn   net.Conn
 	hijackedConn   net.Conn
 	staleReader    *bufio.Reader
 	staleReader    *bufio.Reader
@@ -144,6 +152,10 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 	req.Close = false
 	req.Close = false
 	req.Header.Set("User-Agent", "")
 	req.Header.Set("User-Agent", "")
 
 
+	for k, s := range pc.customHeaders {
+		req.Header[k] = s
+	}
+
 	if pc.authState == HTTP_AUTH_STATE_CHALLENGED {
 	if pc.authState == HTTP_AUTH_STATE_CHALLENGED {
 		err := pc.authenticator.Authenticate(req, pc.authResponse)
 		err := pc.authenticator.Authenticate(req, pc.authResponse)
 		if err != nil {
 		if err != nil {

+ 13 - 6
psiphon/upstreamproxy/transport_proxy_auth.go

@@ -43,14 +43,15 @@ type ProxyAuthTransport struct {
 	Password      string
 	Password      string
 	Authenticator HttpAuthenticator
 	Authenticator HttpAuthenticator
 	mu            sync.Mutex
 	mu            sync.Mutex
+	CustomHeaders http.Header
 }
 }
 
 
-func NewProxyAuthTransport(rawTransport *http.Transport) (*ProxyAuthTransport, error) {
+func NewProxyAuthTransport(rawTransport *http.Transport, customHeaders http.Header) (*ProxyAuthTransport, error) {
 	dialFn := rawTransport.Dial
 	dialFn := rawTransport.Dial
 	if dialFn == nil {
 	if dialFn == nil {
 		dialFn = net.Dial
 		dialFn = net.Dial
 	}
 	}
-	tr := &ProxyAuthTransport{Dial: dialFn}
+	tr := &ProxyAuthTransport{Dial: dialFn, CustomHeaders: customHeaders}
 	proxyUrlFn := rawTransport.Proxy
 	proxyUrlFn := rawTransport.Proxy
 	if proxyUrlFn != nil {
 	if proxyUrlFn != nil {
 		wrappedDialFn := tr.wrapTransportDial()
 		wrappedDialFn := tr.wrapTransportDial()
@@ -96,8 +97,9 @@ func (tr *ProxyAuthTransport) RoundTrip(req *http.Request) (resp *http.Response,
 
 
 	var ha HttpAuthenticator = nil
 	var ha HttpAuthenticator = nil
 
 
-	//Clone request early because RoundTrip will destroy request Body
-	newReq := cloneRequest(req)
+	// Clone request early because RoundTrip will destroy request Body
+	// Also add custom headers to the cloned request
+	newReq := cloneRequest(req, tr.CustomHeaders)
 
 
 	resp, err = tr.Transport.RoundTrip(newReq)
 	resp, err = tr.Transport.RoundTrip(newReq)
 
 
@@ -118,7 +120,7 @@ func (tr *ProxyAuthTransport) RoundTrip(req *http.Request) (resp *http.Response,
 		tr.Authenticator = ha
 		tr.Authenticator = ha
 	authenticationLoop:
 	authenticationLoop:
 		for {
 		for {
-			newReq = cloneRequest(req)
+			newReq = cloneRequest(req, tr.CustomHeaders)
 			err = tr.Authenticator.Authenticate(newReq, resp)
 			err = tr.Authenticator.Authenticate(newReq, resp)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
@@ -156,7 +158,7 @@ func (tr *ProxyAuthTransport) wrapTransportDial() DialFunc {
 	}
 	}
 }
 }
 
 
-func cloneRequest(r *http.Request) *http.Request {
+func cloneRequest(r *http.Request, ch http.Header) *http.Request {
 	// shallow copy of the struct
 	// shallow copy of the struct
 	r2 := new(http.Request)
 	r2 := new(http.Request)
 	*r2 = *r
 	*r2 = *r
@@ -166,6 +168,11 @@ func cloneRequest(r *http.Request) *http.Request {
 		r2.Header[k] = s
 		r2.Header[k] = s
 	}
 	}
 
 
+	//Add custom headers to the cloned request
+	for k, s := range ch {
+		r2.Header[k] = s
+	}
+
 	if r.Body != nil {
 	if r.Body != nil {
 		body, _ := ioutil.ReadAll(r.Body)
 		body, _ := ioutil.ReadAll(r.Body)
 		defer r.Body.Close()
 		defer r.Body.Close()

+ 4 - 1
psiphon/upstreamproxy/upstreamproxy.go

@@ -21,9 +21,11 @@ package upstreamproxy
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"golang.org/x/net/proxy"
 	"net"
 	"net"
+	"net/http"
 	"net/url"
 	"net/url"
+
+	"golang.org/x/net/proxy"
 )
 )
 
 
 type DialFunc func(string, string) (net.Conn, error)
 type DialFunc func(string, string) (net.Conn, error)
@@ -43,6 +45,7 @@ func proxyError(err error) error {
 type UpstreamProxyConfig struct {
 type UpstreamProxyConfig struct {
 	ForwardDialFunc DialFunc
 	ForwardDialFunc DialFunc
 	ProxyURIString  string
 	ProxyURIString  string
+	CustomHeaders   http.Header
 }
 }
 
 
 // UpstreamProxyConfig implements proxy.Dialer interface
 // UpstreamProxyConfig implements proxy.Dialer interface