|
@@ -9,7 +9,6 @@ import (
|
|
|
"net/http"
|
|
|
"net/http/httputil"
|
|
|
"net/url"
|
|
|
- "strings"
|
|
|
"time"
|
|
|
|
|
|
"github.com/docker/docker/api/types"
|
|
@@ -17,21 +16,6 @@ import (
|
|
|
"github.com/pkg/errors"
|
|
|
)
|
|
|
|
|
|
-// tlsClientCon holds tls information and a dialed connection.
|
|
|
-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 conn, ok := c.rawConn.(types.CloseWriter); ok {
|
|
|
- return conn.CloseWrite()
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
// postHijacked sends a POST request and hijacks the connection.
|
|
|
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
|
|
|
bodyEncoded, err := encodeData(body)
|
|
@@ -54,96 +38,9 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
|
|
|
return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err
|
|
|
}
|
|
|
|
|
|
-func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
|
|
|
- return tlsDialWithDialer(new(net.Dialer), network, addr, config)
|
|
|
-}
|
|
|
-
|
|
|
-// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in
|
|
|
-// order to return our custom tlsClientCon struct which holds both the tls.Conn
|
|
|
-// object _and_ its underlying raw connection. The rationale for this is that
|
|
|
-// we need to be able to close the write end of the connection when attaching,
|
|
|
-// which tls.Conn does not provide.
|
|
|
-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("")
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- proxyDialer, err := sockets.DialerFromEnvironment(dialer)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- rawConn, err := proxyDialer.Dial(network, addr)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- // When we set up a TCP connection for hijack, there could be long periods
|
|
|
- // of inactivity (a long running command with no output) that in certain
|
|
|
- // network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
|
|
- // state. Setting TCP KeepAlive on the socket connection will prohibit
|
|
|
- // ECONNTIMEOUT unless the socket connection truly is broken
|
|
|
- if tcpConn, ok := rawConn.(*net.TCPConn); ok {
|
|
|
- tcpConn.SetKeepAlive(true)
|
|
|
- tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
|
|
- }
|
|
|
-
|
|
|
- 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 = tlsConfigClone(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
|
|
|
-}
|
|
|
-
|
|
|
func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
|
|
|
if tlsConfig != nil && proto != "unix" && proto != "npipe" {
|
|
|
- // Notice this isn't Go standard's tls.Dial function
|
|
|
- return tlsDial(proto, addr, tlsConfig)
|
|
|
+ return tls.Dial(proto, addr, tlsConfig)
|
|
|
}
|
|
|
if proto == "npipe" {
|
|
|
return sockets.DialPipe(addr, 32*time.Second)
|