Repository: streamlink/streamlink

Issue:
Need a option for DNS through proxy
### Checklist

- [ ] This is a bug report.
- [x] This is a feature request.
- [ ] This is a plugin (improvement) request.
- [ ] I have read the contribution guidelines.

### Description
Steamlink can't resolve hostnames through the proxy server. If you receive a incorrect ip of the host via DNS spoofing, the option --http-proxy and --https-proxy may not work correctly.

### Expected / Actual behavior
Expected: Steamkink connect to youtube server through proxy and work fine.
Actual: Read timed out.

### Reproduction steps / Explicit stream URLs to test
1. Run a socks5 proxy server on port 1080
2. streamlink --hls-live-restart --http-proxy "socks5://127.0.0.1:1080" --https-proxy "socks5://127.0.0.1:1080" https://www.youtube.com/watch?v=fO8x9MZ8m9g

### Logs
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.6.5
[cli][debug] Streamlink: 0.12.1
[cli][debug] Requests(2.18.4), Socks(1.6.7), Websocket(0.47.0)
[cli][info] Found matching plugin youtube for URL https://www.youtube.com/watch?v=fO8x9MZ8m9g
error: Unable to open URL: https://youtube.com/get_video_info (SOCKSHTTPSConnectionPool(host='youtube.com', port=443): Read timed out. (read timeout=20.0))

### Comments, screenshots, etc.
This is the log of my socks5 proxy when I run the step 2:
[2018-05-15 21:26:52] connect to 8.7.198.45:443

And this is the log when I visit https://youtube.com on firefox(Use the same socks5 proxy and it works fine)
[2018-05-15 21:28:51] connect to youtube.com:443

And this is the ping log to youtube.com on my pc
Pinging youtube.com [8.7.198.45] with 32 bytes of data:
Request timed out.
Request timed out.

Stramlink resolve the hostname 'youtube.com' direct and receive the incorrect ip '8.7.198.45' via DNS spoofing. Firefox works fine because it send the DNS request through the proxy.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/streamlink/streamlink/pull/1655?src=pr&el=h1) Report
> Merging [#1655](https://codecov.io/gh/streamlink/streamlink/pull/1655?src=pr&el=desc) into [master](https://codecov.io/gh/streamlink/streamlink/commit/e2a55461decc6856912325e8103cefb359027811?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `n/a`.


```diff
@@            Coverage Diff             @@
##           master    #1655      +/-   ##
==========================================
+ Coverage   33.16%   33.17%   +<.01%     
==========================================
  Files         229      229              
  Lines       12898    12898              
==========================================
+ Hits         4278     4279       +1     
+ Misses       8620     8619       -1
```

gravyboat: @beardypig This is a good addition, what do you think about adding an example of `socks4a` or `socks5h` just in case there are any language barriers that could result in confusion:

```
.. code-block:: console
 
     $ streamlink --http-proxy "socks5h://10.10.1.10:3128/" --https-proxy "socks5h://10.10.1.10:1242"
```
beardypig: Could add another example :) Where do you suggest? After the note? Or do we remove the note and just expand the socks explanation?

<sub>Sent with <a href="http://githawk.com">GitHawk</a></sub>
gravyboat: @beardypig I think keeping the note is important for clarity, so I'd say just after the note.
beardypig: I moved the note to above the examples, and added an example for `socks5h/socks4a`.

<img width="730" alt="socks-docs-preview" src="https://user-images.githubusercontent.com/16033421/40117387-1e1c3496-5917-11e8-91bb-bfca7c0845c4.png">


bastimeyer: Is there a requests doc site other than
https://github.com/requests/requests/blob/master/docs/user/advanced.rst#socks
which could be added here as an additional source of information?
Their external docs site is served via regular http and doesn't contain any information about the socks proxy usage :unamused: 

Patch:
diff --git a/docs/cli.rst b/docs/cli.rst
index 90a91512..e96b1c8b 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -337,14 +337,19 @@ change the proxy server that Streamlink will use for HTTP and HTTPS requests res
 As HTTP and HTTPS requests can be handled by separate proxies, you may need to specify both
 options if the plugin you use makes HTTP and HTTPS requests.
 
-Both HTTP and SOCKS5 proxies are supported, authentication is supported for both types.
+Both HTTP and SOCKS proxies are supported, authentication is supported for both types.
+
+.. note::
+    When using a SOCKS proxy the ``socks4`` and ``socks5`` schemes mean that DNS lookups are done
+    locally, rather than on the proxy server. To have the proxy server perform the DNS lookups, the
+    ``socks4a`` and ``socks5h`` schemes should be used instead.
 
 For example:
 
 .. code-block:: console
 
     $ streamlink --http-proxy "http://user:pass@10.10.1.10:3128/" --https-proxy "socks5://10.10.1.10:1242"
-
+    $ streamlink --http-proxy "socks4a://10.10.1.10:1235" --https-proxy "socks5h://10.10.1.10:1234"
 
 Command-line usage
 ------------------
diff --git a/src/streamlink/plugins/tf1.py b/src/streamlink/plugins/tf1.py
index 189f124c..88b5e585 100644
--- a/src/streamlink/plugins/tf1.py
+++ b/src/streamlink/plugins/tf1.py
@@ -9,13 +9,20 @@ from streamlink.stream import HLSStream
 
 
 class TF1(Plugin):
-    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/(tf1|tmc|tfx|tf1-series-films)/direct|(lci).fr/direct)/?")
+    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/([\w-]+)/direct|(lci).fr/direct)/?")
     embed_url = "http://www.wat.tv/embedframe/live{0}"
     embed_re = re.compile(r"urlLive.*?:.*?\"(http.*?)\"", re.MULTILINE)
     api_url = "http://www.wat.tv/get/{0}/591997"
     swf_url = "http://www.wat.tv/images/v70/PlayerLite.swf"
-    hds_channel_remap = {"tf1": "androidliveconnect", "lci": "androidlivelci", "tfx" : "nt1live", "tf1-series-films" : "hd1live" }
-    hls_channel_remap = {"lci": "LCI", "tf1": "V4", "tfx" : "nt1", "tf1-series-films" : "hd1" }
+    hds_channel_remap = {"tf1": "androidliveconnect",
+                         "lci": "androidlivelci",
+                         "tfx": "nt1live",
+                         "hd1": "hd1live",  # renamed to tfx
+                         "tf1-series-films": "hd1live"}
+    hls_channel_remap = {"lci": "LCI",
+                         "tf1": "V4",
+                         "tfx": "nt1",
+                         "tf1-series-films": "hd1"}
 
     @classmethod
     def can_handle_url(cls, url):
@@ -23,6 +30,7 @@ class TF1(Plugin):
 
     def _get_hds_streams(self, channel):
         channel = self.hds_channel_remap.get(channel, "{0}live".format(channel))
+        self.logger.debug("Using HDS channel name: {0}".format(channel))
         manifest_url = http.get(self.api_url.format(channel),
                                 params={"getURL": 1},
                                 headers={"User-Agent": useragents.FIREFOX}).text


Test patch:
diff --git a/tests/test_plugin_tf1.py b/tests/test_plugin_tf1.py
index 77afd8d8..f8e48790 100644
--- a/tests/test_plugin_tf1.py
+++ b/tests/test_plugin_tf1.py
@@ -12,11 +12,11 @@ class TestPluginTF1(unittest.TestCase):
         self.assertTrue(TF1.can_handle_url("http://lci.fr/direct"))
         self.assertTrue(TF1.can_handle_url("http://www.lci.fr/direct"))
         self.assertTrue(TF1.can_handle_url("http://tf1.fr/tmc/direct"))
+        self.assertTrue(TF1.can_handle_url("http://tf1.fr/lci/direct"))
 
+    def test_can_handle_url_negative(self):
         # shouldn't match
         self.assertFalse(TF1.can_handle_url("http://tf1.fr/direct"))
-#       self.assertFalse(TF1.can_handle_url("http://tf1.fr/nt1/direct")) NOTE : TF1 redirect old channel names to new ones (for now).
-#       self.assertFalse(TF1.can_handle_url("http://tf1.fr/hd1/direct"))
         self.assertFalse(TF1.can_handle_url("http://www.tf1.fr/direct"))
         self.assertFalse(TF1.can_handle_url("http://www.tvcatchup.com/"))
         self.assertFalse(TF1.can_handle_url("http://www.youtube.com/"))