// Dial dials upstream using domain-fronting. func (d *dialer) Dial(network, addr string) (net.Conn, error) { if !strings.Contains(network, "tcp") { return nil, fmt.Errorf("Protocol %s is not supported, only tcp is supported", network) } return enproxy.Dial(addr, d.enproxyConfig) }
// Intercept intercepts a CONNECT request, hijacks the underlying client // connetion and starts piping the data over a new enproxy.Conn configured using // this Config. func (c *ClientHandler) Intercept(resp http.ResponseWriter, req *http.Request) { if req.Method != "CONNECT" { panic("Intercept used for non-CONNECT request!") } // Hijack underlying connection clientConn, _, err := resp.(http.Hijacker).Hijack() if err != nil { resp.WriteHeader(502) fmt.Fprintf(resp, "Unable to hijack connection: %s", err) return } defer clientConn.Close() addr := hostIncludingPort(req, 443) // Establish outbound connection connOut, err := enproxy.Dial(addr, c.Config) if err != nil { resp.WriteHeader(502) fmt.Fprintf(resp, "Unable to open enproxy connection: %s", err) return } defer connOut.Close() // Pipe data pipeData(clientConn, connOut, req) }
func (d *dialer) HttpClientUsing(masquerade *Masquerade) *http.Client { enproxyConfig := d.enproxyConfigWith(func(addr string) (net.Conn, error) { return d.dialServerWith(masquerade) }) return &http.Client{ Transport: &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { return enproxy.Dial(addr, enproxyConfig) }, }, } }
// resetProxiedClient reconfigures the host so attempts to proxy through it, // for the sake of checking whether it's up and serving, will use the given // port. Valid port values are "443", in which case we'll access the proxy // through HTTPS, or "80", for an unencrypted connection. // // This is necessary because when peerscanner starts up and gets the list of // hosts from the various DNS/CDN services, it has no way to know what port // these servers are listening at. So we want to try both until one works, or // until the server first registers with peerscanner, advertising which port // it uses. func (h *host) resetProxiedClient(port string) { var dial func(addr string) (net.Conn, error) if port == "80" { dial = func(addr string) (net.Conn, error) { dialer := net.Dialer{Timeout: dialTimeout} return dialer.Dial("tcp", h.ip+":80") } } else if port == "443" { dial = func(addr string) (net.Conn, error) { return tlsdialer.DialWithDialer(&net.Dialer{ Timeout: dialTimeout, }, "tcp", h.ip+":443", true, &tls.Config{ InsecureSkipVerify: true, // Cache TLS sessions ClientSessionCache: tls.NewLRUClientSessionCache(1000), }) } } else { log.Errorf("Unsupported port: %v", port) return } h.proxiedClient = &http.Client{ Transport: &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { return enproxy.Dial(addr, &enproxy.Config{ DialProxy: dial, NewRequest: func(upstreamHost string, method string, body io.Reader) (req *http.Request, err error) { return http.NewRequest(method, "http://"+h.ip+"/", body) }, OnFirstResponse: func(resp *http.Response) { h.reportedHostMutex.Lock() h.reportedHost = resp.Header.Get(enproxy.X_ENPROXY_PROXY_HOST) h.reportedHostMutex.Unlock() }, }) }, DisableKeepAlives: true, }, Timeout: requestTimeout, } }
func main() { if len(os.Args) < 2 { log.Fatal("Usage: client <proxy addr to listen> <proxy server addr>") } enproxyConfig := &enproxy.Config{ DialProxy: func(addr string) (net.Conn, error) { return net.Dial("tcp", os.Args[2]) }, NewRequest: func(host string, method string, body io.Reader) (req *http.Request, err error) { if host == "" { host = os.Args[2] } return http.NewRequest(method, "http://"+host+"/", body) }, } httpServer := &http.Server{ Addr: os.Args[1], Handler: &ClientHandler{ ProxyAddr: os.Args[2], Config: enproxyConfig, ReverseProxy: &httputil.ReverseProxy{ Director: func(req *http.Request) { // do nothing }, Transport: &http.Transport{ Dial: func(network string, addr string) (net.Conn, error) { return enproxy.Dial(addr, enproxyConfig) }, }, }, }, } err := httpServer.ListenAndServe() if err != nil { log.Fatal(err) } }