summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Udvare <audvare@gmail.com>2018-03-16 20:11:47 -0400
committerSergey M․ <dstftw@gmail.com>2018-08-29 01:17:53 +0700
commit8959018a5f3efc02f7402c44f4d3950c0811b97a (patch)
tree1eb98dfb63cb17ef0c678f1c287ab74b57274350
parenteebbce5656fd7d626594aa54d2d1ec545d9ae53a (diff)
downloadyoutube-dl-8959018a5f3efc02f7402c44f4d3950c0811b97a.tar.gz
youtube-dl-8959018a5f3efc02f7402c44f4d3950c0811b97a.tar.xz
youtube-dl-8959018a5f3efc02f7402c44f4d3950c0811b97a.zip
[utils] Skip remote IP addresses non matching to source address' IP version (closes #13422)
-rw-r--r--youtube_dl/utils.py33
1 files changed, 33 insertions, 0 deletions
diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py
index 0c830ba71..2be8c95cd 100644
--- a/youtube_dl/utils.py
+++ b/youtube_dl/utils.py
@@ -882,7 +882,40 @@ def _create_http_connection(ydl_handler, http_class, is_https, *args, **kwargs):
         kwargs['strict'] = True
     hc = http_class(*args, **compat_kwargs(kwargs))
     source_address = ydl_handler._params.get('source_address')
+
     if source_address is not None:
+        filter_for = socket.AF_INET if '.' in source_address else socket.AF_INET6
+        # This is to workaround _create_connection() from socket where it will try all
+        # address data from getaddrinfo() including IPv6. This filters the result from
+        # getaddrinfo() based on the source_address value.
+        # This is based on the cpython socket.create_connection() function.
+        # https://github.com/python/cpython/blob/master/Lib/socket.py#L691
+        def _create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
+            host, port = address
+            err = None
+            addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
+            ip_addrs = [addr for addr in addrs if addr[0] == filter_for]
+            for res in ip_addrs:
+                af, socktype, proto, canonname, sa = res
+                sock = None
+                try:
+                    sock = socket.socket(af, socktype, proto)
+                    if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
+                        sock.settimeout(timeout)
+                    sock.bind(source_address)
+                    sock.connect(sa)
+                    err = None  # Explicitly break reference cycle
+                    return sock
+                except socket.error as _:
+                    err = _
+                    if sock is not None:
+                        sock.close()
+            if err is not None:
+                raise err
+            else:
+                raise socket.error('Unknown error occurred')
+        hc._create_connection = _create_connection
+
         sa = (source_address, 0)
         if hasattr(hc, 'source_address'):  # Python 2.7+
             hc.source_address = sa