// Dialer creates a *balancer.Dialer backed by a chained server. func (s *ChainedServerInfo) Dialer() (*balancer.Dialer, error) { netd := &net.Dialer{Timeout: chainedDialTimeout} var dial func() (net.Conn, error) if s.Cert == "" { log.Error("No Cert configured for chained server, will dial with plain tcp") dial = func() (net.Conn, error) { return netd.Dial("tcp", s.Addr) } } else { log.Trace("Cert configured for chained server, will dial with tls over tcp") cert, err := keyman.LoadCertificateFromPEMBytes([]byte(s.Cert)) if err != nil { return nil, fmt.Errorf("Unable to parse certificate: %s", err) } x509cert := cert.X509() sessionCache := tls.NewLRUClientSessionCache(1000) dial = func() (net.Conn, error) { conn, err := tlsdialer.DialWithDialer(netd, "tcp", s.Addr, false, &tls.Config{ ClientSessionCache: sessionCache, InsecureSkipVerify: true, }) if err != nil { return nil, err } if !conn.ConnectionState().PeerCertificates[0].Equal(x509cert) { conn.Close() return nil, fmt.Errorf("Server's certificate didn't match expected!") } return conn, err } } ccfg := chained.Config{ DialServer: dial, } if s.AuthToken != "" { ccfg.OnRequest = func(req *http.Request) { req.Header.Set("X-LANTERN-AUTH-TOKEN", s.AuthToken) } } d := chained.NewDialer(ccfg) // Is this a trusted proxy that we could use for HTTP traffic? var trusted string if s.Trusted { trusted = "(trusted) " } return &balancer.Dialer{ Label: fmt.Sprintf("%schained proxy at %s", trusted, s.Addr), Weight: s.Weight, QOS: s.QOS, Trusted: s.Trusted, Dial: func(network, addr string) (net.Conn, error) { return withStats(d.Dial(network, addr)) }, }, nil }
// 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, } }
// Dialer creates a *balancer.Dialer backed by a chained server. func (s *ChainedServerInfo) Dialer(deviceID string) (*balancer.Dialer, error) { netd := &net.Dialer{Timeout: chainedDialTimeout} forceProxy := ForceChainedProxyAddr != "" addr := s.Addr if forceProxy { log.Errorf("Forcing proxying to server at %v instead of configured server at %v", ForceChainedProxyAddr, s.Addr) addr = ForceChainedProxyAddr } var dial func() (net.Conn, error) if s.Cert == "" && !forceProxy { log.Error("No Cert configured for chained server, will dial with plain tcp") dial = func() (net.Conn, error) { return netd.Dial("tcp", addr) } } else { log.Trace("Cert configured for chained server, will dial with tls over tcp") cert, err := keyman.LoadCertificateFromPEMBytes([]byte(s.Cert)) if err != nil { return nil, fmt.Errorf("Unable to parse certificate: %s", err) } x509cert := cert.X509() sessionCache := tls.NewLRUClientSessionCache(1000) dial = func() (net.Conn, error) { conn, err := tlsdialer.DialWithDialer(netd, "tcp", addr, false, &tls.Config{ ClientSessionCache: sessionCache, InsecureSkipVerify: true, }) if err != nil { return nil, err } if !forceProxy && !conn.ConnectionState().PeerCertificates[0].Equal(x509cert) { if err := conn.Close(); err != nil { log.Debugf("Error closing chained server connection: %s", err) } return nil, fmt.Errorf("Server's certificate didn't match expected!") } return conn, err } } // Is this a trusted proxy that we could use for HTTP traffic? var trusted string if s.Trusted { trusted = "(trusted) " } label := fmt.Sprintf("%schained proxy at %s", trusted, addr) ccfg := chained.Config{ DialServer: dial, Label: label, } authToken := s.AuthToken if ForceAuthToken != "" { authToken = ForceAuthToken } ccfg.OnRequest = func(req *http.Request) { if authToken != "" { req.Header.Set("X-LANTERN-AUTH-TOKEN", authToken) } req.Header.Set("X-LANTERN-DEVICE-ID", deviceID) } d := chained.NewDialer(ccfg) return &balancer.Dialer{ Label: label, Weight: s.Weight, QOS: s.QOS, Trusted: s.Trusted, Dial: func(network, addr string) (net.Conn, error) { conn, err := d.Dial(network, addr) if err != nil { return conn, err } conn = idletiming.Conn(conn, idleTimeout, func() { log.Debugf("Proxy connection to %s via %s idle for %v, closing", addr, conn.RemoteAddr(), idleTimeout) if err := conn.Close(); err != nil { log.Debugf("Unable to close connection: %v", err) } }) return conn, nil }, AuthToken: authToken, }, nil }