// 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 }
// 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 }