// Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // The content is borrowed from Docker's own source code to provide a simple // tls based dialer package docker import ( "crypto/tls" "errors" "net" "strings" "time" ) type tlsClientCon struct { *tls.Conn rawConn net.Conn } func (c *tlsClientCon) CloseWrite() error { // Go standard tls.Conn doesn't provide the CloseWrite() method so we do it // on its underlying connection. if cwc, ok := c.rawConn.(interface { CloseWrite() error }); ok { return cwc.CloseWrite() } return nil } func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) { // We want the Timeout and Deadline values from dialer to cover the // whole process: TCP connection and TLS handshake. This means that we // also need to start our own timers now. timeout := dialer.Timeout if !dialer.Deadline.IsZero() { deadlineTimeout := time.Until(dialer.Deadline) if timeout == 0 || deadlineTimeout < timeout { timeout = deadlineTimeout } } var errChannel chan error if timeout != 0 { errChannel = make(chan error, 2) time.AfterFunc(timeout, func() { errChannel <- errors.New("") }) } rawConn, err := dialer.Dial(network, addr) if err != nil { return nil, err } colonPos := strings.LastIndex(addr, ":") if colonPos == -1 { colonPos = len(addr) } hostname := addr[:colonPos] // If no ServerName is set, infer the ServerName // from the hostname we're connecting to. if config.ServerName == "" { // Make a copy to avoid polluting argument or default. config = copyTLSConfig(config) config.ServerName = hostname } conn := tls.Client(rawConn, config) if timeout == 0 { err = conn.Handshake() } else { go func() { errChannel <- conn.Handshake() }() err = <-errChannel } if err != nil { rawConn.Close() return nil, err } // This is Docker difference with standard's crypto/tls package: returned a // wrapper which holds both the TLS and raw connections. return &tlsClientCon{conn, rawConn}, nil } // this exists to silent an error message in go vet func copyTLSConfig(cfg *tls.Config) *tls.Config { return &tls.Config{ Certificates: cfg.Certificates, CipherSuites: cfg.CipherSuites, ClientAuth: cfg.ClientAuth, ClientCAs: cfg.ClientCAs, ClientSessionCache: cfg.ClientSessionCache, CurvePreferences: cfg.CurvePreferences, InsecureSkipVerify: cfg.InsecureSkipVerify, MaxVersion: cfg.MaxVersion, MinVersion: cfg.MinVersion, NextProtos: cfg.NextProtos, Rand: cfg.Rand, RootCAs: cfg.RootCAs, ServerName: cfg.ServerName, SessionTicketsDisabled: cfg.SessionTicketsDisabled, } }