summaryrefslogtreecommitdiff
path: root/docker/utils
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2018-11-01 14:57:29 -0700
committerJoffrey F <joffrey@docker.com>2018-11-01 15:44:43 -0700
commitf302756599a61d6775fbdf2beab8f1de7e0022c4 (patch)
tree306d5ed162f68fc86cd6acb9a78043b830787cdd /docker/utils
parent6bfe2005e0a700621c094a01b42db39e7c6408de (diff)
downloaddocker-py-f302756599a61d6775fbdf2beab8f1de7e0022c4.tar.gz
Rewrite utils.parse_host to detect more invalid addresses.ssh_protocol_support
The method now uses parsing methods from urllib to better split provided URLs. Addresses containing query strings, parameters, passwords or fragments no longer fail silently. SSH addresses containing paths are no longer accepted. Signed-off-by: Joffrey F <joffrey@docker.com>
Diffstat (limited to 'docker/utils')
-rw-r--r--docker/utils/utils.py130
1 files changed, 72 insertions, 58 deletions
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index f8f7123..4e04caf 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -1,10 +1,11 @@
import base64
+import json
import os
import os.path
-import json
import shlex
-from distutils.version import StrictVersion
+import string
from datetime import datetime
+from distutils.version import StrictVersion
import six
@@ -13,11 +14,12 @@ from .. import tls
if six.PY2:
from urllib import splitnport
+ from urlparse import urlparse
else:
- from urllib.parse import splitnport
+ from urllib.parse import splitnport, urlparse
DEFAULT_HTTP_HOST = "127.0.0.1"
-DEFAULT_UNIX_SOCKET = "http+unix://var/run/docker.sock"
+DEFAULT_UNIX_SOCKET = "http+unix:///var/run/docker.sock"
DEFAULT_NPIPE = 'npipe:////./pipe/docker_engine'
BYTE_UNITS = {
@@ -212,81 +214,93 @@ def parse_repository_tag(repo_name):
return repo_name, None
-# Based on utils.go:ParseHost http://tinyurl.com/nkahcfh
-# fd:// protocol unsupported (for obvious reasons)
-# Added support for http and https
-# Protocol translation: tcp -> http, unix -> http+unix
def parse_host(addr, is_win32=False, tls=False):
- proto = "http+unix"
- port = None
path = ''
+ port = None
+ host = None
+ # Sensible defaults
if not addr and is_win32:
- addr = DEFAULT_NPIPE
-
+ return DEFAULT_NPIPE
if not addr or addr.strip() == 'unix://':
return DEFAULT_UNIX_SOCKET
addr = addr.strip()
- if addr.startswith('http://'):
- addr = addr.replace('http://', 'tcp://')
- if addr.startswith('http+unix://'):
- addr = addr.replace('http+unix://', 'unix://')
- if addr == 'tcp://':
+ parsed_url = urlparse(addr)
+ proto = parsed_url.scheme
+ if not proto or any([x not in string.ascii_letters + '+' for x in proto]):
+ # https://bugs.python.org/issue754016
+ parsed_url = urlparse('//' + addr, 'tcp')
+ proto = 'tcp'
+
+ if proto == 'fd':
+ raise errors.DockerException('fd protocol is not implemented')
+
+ # These protos are valid aliases for our library but not for the
+ # official spec
+ if proto == 'http' or proto == 'https':
+ tls = proto == 'https'
+ proto = 'tcp'
+ elif proto == 'http+unix':
+ proto = 'unix'
+
+ if proto not in ('tcp', 'unix', 'npipe', 'ssh'):
raise errors.DockerException(
- "Invalid bind address format: {0}".format(addr)
+ "Invalid bind address protocol: {}".format(addr)
)
- elif addr.startswith('unix://'):
- addr = addr[7:]
- elif addr.startswith('tcp://'):
- proto = 'http{0}'.format('s' if tls else '')
- addr = addr[6:]
- elif addr.startswith('https://'):
- proto = "https"
- addr = addr[8:]
- elif addr.startswith('npipe://'):
- proto = 'npipe'
- addr = addr[8:]
- elif addr.startswith('fd://'):
- raise errors.DockerException("fd protocol is not implemented")
- elif addr.startswith('ssh://'):
- proto = 'ssh'
- addr = addr[6:]
- else:
- if "://" in addr:
- raise errors.DockerException(
- "Invalid bind address protocol: {0}".format(addr)
- )
- proto = "https" if tls else "http"
- if proto in ("http", "https", "ssh"):
- address_parts = addr.split('/', 1)
- host = address_parts[0]
- if len(address_parts) == 2:
- path = '/' + address_parts[1]
- host, port = splitnport(host)
+ if proto == 'tcp' and not parsed_url.netloc:
+ # "tcp://" is exceptionally disallowed by convention;
+ # omitting a hostname for other protocols is fine
+ raise errors.DockerException(
+ 'Invalid bind address format: {}'.format(addr)
+ )
+ if any([
+ parsed_url.params, parsed_url.query, parsed_url.fragment,
+ parsed_url.password
+ ]):
+ raise errors.DockerException(
+ 'Invalid bind address format: {}'.format(addr)
+ )
+
+ if parsed_url.path and proto == 'ssh':
+ raise errors.DockerException(
+ 'Invalid bind address format: no path allowed for this protocol:'
+ ' {}'.format(addr)
+ )
+ else:
+ path = parsed_url.path
+ if proto == 'unix' and parsed_url.hostname is not None:
+ # For legacy reasons, we consider unix://path
+ # to be valid and equivalent to unix:///path
+ path = '/'.join((parsed_url.hostname, path))
+
+ if proto in ('tcp', 'ssh'):
+ # parsed_url.hostname strips brackets from IPv6 addresses,
+ # which can be problematic hence our use of splitnport() instead.
+ host, port = splitnport(parsed_url.netloc)
if port is None or port < 0:
- if proto == 'ssh':
- port = 22
- else:
+ if proto != 'ssh':
raise errors.DockerException(
- "Invalid port: {0}".format(addr)
+ 'Invalid bind address format: port is required:'
+ ' {}'.format(addr)
)
+ port = 22
if not host:
host = DEFAULT_HTTP_HOST
- else:
- host = addr
- if proto in ("http", "https") and port == -1:
- raise errors.DockerException(
- "Bind address needs a port: {0}".format(addr))
+ # Rewrite schemes to fit library internals (requests adapters)
+ if proto == 'tcp':
+ proto = 'http{}'.format('s' if tls else '')
+ elif proto == 'unix':
+ proto = 'http+unix'
- if proto == "http+unix" or proto == 'npipe':
- return "{0}://{1}".format(proto, host).rstrip('/')
- return "{0}://{1}:{2}{3}".format(proto, host, port, path).rstrip('/')
+ if proto in ('http+unix', 'npipe'):
+ return "{}://{}".format(proto, path).rstrip('/')
+ return '{0}://{1}:{2}{3}'.format(proto, host, port, path).rstrip('/')
def parse_devices(devices):