119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
|
// 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 := dialer.Deadline.Sub(time.Now())
|
||
|
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,
|
||
|
NameToCertificate: cfg.NameToCertificate,
|
||
|
NextProtos: cfg.NextProtos,
|
||
|
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||
|
Rand: cfg.Rand,
|
||
|
RootCAs: cfg.RootCAs,
|
||
|
ServerName: cfg.ServerName,
|
||
|
SessionTicketKey: cfg.SessionTicketKey,
|
||
|
SessionTicketsDisabled: cfg.SessionTicketsDisabled,
|
||
|
}
|
||
|
}
|