opts/hosts.go
4f0d95fa
 package opts // import "github.com/docker/docker/opts"
9b995910
 
 import (
 	"fmt"
 	"net"
 	"net/url"
ec87479b
 	"path/filepath"
9b995910
 	"strconv"
 	"strings"
ec87479b
 
 	"github.com/docker/docker/pkg/homedir"
9b995910
 )
 
 var (
62cc802f
 	// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp://
9b995910
 	// These are the IANA registered port numbers for use with Docker
 	// see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker
 	DefaultHTTPPort = 2375 // Default HTTP Port
 	// DefaultTLSHTTPPort Default HTTP Port used when TLS enabled
 	DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port
 	// DefaultUnixSocket Path for the unix socket.
 	// Docker daemon by default always listens on the default unix socket
 	DefaultUnixSocket = "/var/run/docker.sock"
 	// DefaultTCPHost constant defines the default host string used by docker on Windows
 	DefaultTCPHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
 	// DefaultTLSHost constant defines the default host string used by docker for TLS sockets
 	DefaultTLSHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultTLSHTTPPort)
0906195f
 	// DefaultNamedPipe defines the default named pipe used by docker on Windows
 	DefaultNamedPipe = `//./pipe/docker_engine`
9b995910
 )
 
 // ValidateHost validates that the specified string is a valid host and returns it.
 func ValidateHost(val string) (string, error) {
0906195f
 	host := strings.TrimSpace(val)
de5c80b4
 	// The empty string means default and is not handled by parseDaemonHost
0906195f
 	if host != "" {
de5c80b4
 		_, err := parseDaemonHost(host)
0906195f
 		if err != nil {
 			return val, err
 		}
9b995910
 	}
 	// Note: unlike most flag validators, we don't return the mutated value here
113cae5b
 	//       we need to know what the user entered later (using ParseHost) to adjust for TLS
9b995910
 	return val, nil
 }
 
ec87479b
 // ParseHost and set defaults for a Daemon host string.
8493fb18
 // defaultToTLS is preferred over defaultToUnixXDG.
 func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error) {
0906195f
 	host := strings.TrimSpace(val)
 	if host == "" {
 		if defaultToTLS {
 			host = DefaultTLSHost
8493fb18
 		} else if defaultToUnixXDG {
ec87479b
 			runtimeDir, err := homedir.GetRuntimeDir()
 			if err != nil {
 				return "", err
 			}
 			socket := filepath.Join(runtimeDir, "docker.sock")
 			host = "unix://" + socket
0906195f
 		} else {
 			host = DefaultHost
 		}
 	} else {
 		var err error
de5c80b4
 		host, err = parseDaemonHost(host)
0906195f
 		if err != nil {
 			return val, err
 		}
9b995910
 	}
 	return host, nil
 }
 
de5c80b4
 // parseDaemonHost parses the specified address and returns an address that will be used as the host.
0906195f
 // Depending of the address specified, this may return one of the global Default* strings defined in hosts.go.
de5c80b4
 func parseDaemonHost(addr string) (string, error) {
0a4a0d98
 	addrParts := strings.SplitN(addr, "://", 2)
0906195f
 	if len(addrParts) == 1 && addrParts[0] != "" {
9b995910
 		addrParts = []string{"tcp", addrParts[0]}
 	}
 
 	switch addrParts[0] {
 	case "tcp":
fb3eb1c2
 		return ParseTCPAddr(addrParts[1], DefaultTCPHost)
9b995910
 	case "unix":
0906195f
 		return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket)
 	case "npipe":
 		return parseSimpleProtoAddr("npipe", addrParts[1], DefaultNamedPipe)
9b995910
 	case "fd":
 		return addr, nil
 	default:
 		return "", fmt.Errorf("Invalid bind address format: %s", addr)
 	}
 }
 
0906195f
 // parseSimpleProtoAddr parses and validates that the specified address is a valid
 // socket address for simple protocols like unix and npipe. It returns a formatted
 // socket address, either using the address parsed from addr, or the contents of
 // defaultAddr if addr is a blank string.
 func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
 	addr = strings.TrimPrefix(addr, proto+"://")
9b995910
 	if strings.Contains(addr, "://") {
0906195f
 		return "", fmt.Errorf("Invalid proto, expected %s: %s", proto, addr)
9b995910
 	}
 	if addr == "" {
 		addr = defaultAddr
 	}
0906195f
 	return fmt.Sprintf("%s://%s", proto, addr), nil
9b995910
 }
 
fb3eb1c2
 // ParseTCPAddr parses and validates that the specified address is a valid TCP
9b995910
 // address. It returns a formatted TCP address, either using the address parsed
 // from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
 // tryAddr is expected to have already been Trim()'d
 // defaultAddr must be in the full `tcp://host:port` form
fb3eb1c2
 func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
9b995910
 	if tryAddr == "" || tryAddr == "tcp://" {
 		return defaultAddr, nil
 	}
 	addr := strings.TrimPrefix(tryAddr, "tcp://")
 	if strings.Contains(addr, "://") || addr == "" {
 		return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
 	}
 
 	defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
 	defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
 	if err != nil {
 		return "", err
 	}
 	// url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but
 	// not 1.4. See https://github.com/golang/go/issues/12200 and
 	// https://github.com/golang/go/issues/6530.
 	if strings.HasSuffix(addr, "]:") {
 		addr += defaultPort
 	}
 
 	u, err := url.Parse("tcp://" + addr)
 	if err != nil {
 		return "", err
 	}
 	host, port, err := net.SplitHostPort(u.Host)
 	if err != nil {
fb3eb1c2
 		// try port addition once
 		host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
 	}
 	if err != nil {
9b995910
 		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
 	}
 
 	if host == "" {
 		host = defaultHost
 	}
 	if port == "" {
 		port = defaultPort
 	}
 	p, err := strconv.Atoi(port)
 	if err != nil && p == 0 {
 		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
 	}
 
 	return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
 }
c424be21
 
 // ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
 // ExtraHost is in the form of name:ip where the ip has to be a valid ip (IPv4 or IPv6).
 func ValidateExtraHost(val string) (string, error) {
 	// allow for IPv6 addresses in extra hosts by only splitting on first ":"
 	arr := strings.SplitN(val, ":", 2)
 	if len(arr) != 2 || len(arr[0]) == 0 {
 		return "", fmt.Errorf("bad format for add-host: %q", val)
 	}
 	if _, err := ValidateIPAddress(arr[1]); err != nil {
 		return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
 	}
 	return val, nil
 }