[patch] [HUEBridge] proxy support for websockets

Begonnen von ruff, 03 Oktober 2021, 23:14:16

Vorheriges Thema - Nächstes Thema

ruff

Hi,
I'm using deconz+conbee2 but in zero-trust setup hence all networks interfaces are firewalled off and proxy whitelist is used for web policy. And while fhem speaks ok via proxy for rest api, websockets are failing due to trying to make direct connection bypassing proxy.
Hence I came up with this little patch to make use of HttpUtils for socket opening which enables proxy support.


Index: FHEM/30_HUEBridge.pm
===================================================================
--- FHEM/30_HUEBridge.pm        (Revision 25044)
+++ FHEM/30_HUEBridge.pm        (Arbeitskopie)
@@ -371,6 +371,43 @@
   HUEBridge_closeWebsocket($hash);

   my ($host,undef) = split(':',$hash->{host},2);
+
+  if( my $proxy = AttrVal("global", "proxy", undef) ) {
+    $hash->{url} = "http://$host:$hash->{websocketport}/";
+    $hash->{noConnect2} = 1;
+    $hash->{callback} = sub($) {
+      $hash->{conn}->blocking(1);
+      my $pw = AttrVal("global", "proxyAuth", "");                           
+      $pw = "Proxy-Authorization: Basic $pw\r\n" if($pw);                     
+      my $hdr = "CONNECT $host:$hash->{websocketport} HTTP/1.0\r\nUser-Agent: fhem\r\n$pw\r\n";                               
+      syswrite $hash->{conn}, $hdr;
+      my $buf;
+      my $len = sysread($hash->{conn},$buf,65536);
+      if(!defined($len) || $len <= 0 || $buf !~ m/HTTP.*200/) {
+        HttpUtils_Close($hash);
+        Log3 $name, 2, "$name: failed to open websocket via proxy";
+        return;
+      }
+
+      $hash->{CD} = $hash->{conn};
+      delete $hash->{conn};
+      $hash->{PORT} = $hash->{websocketport};
+      my $ret = "GET ws://$host:$hash->{websocketport} HTTP/1.1\r\n";
+      $ret .= HUEBridge_hash2header( {                  'Host' => "$host:$hash->{websocketport}",
+                                                     'Upgrade' => 'websocket',
+                                                  'Connection' => 'Upgrade',
+                                                      'Pragma' => 'no-cache',
+                                               'Cache-Control' => 'no-cache',
+                                           'Sec-WebSocket-Key' => 'RkhFTQ==',
+                                       'Sec-WebSocket-Version' => '13',
+                                     } );
+      $ret .= "\r\n";
+      syswrite($hash->{CD}, $ret );
+    };
+    HttpUtils_Connect($hash);
+    return;
+  }
+
   if( my $socket = IO::Socket::INET->new(PeerAddr=>"$host:$hash->{websocketport}", Timeout=>2, Blocking=>1, ReuseAddr=>1) ) {
     $hash->{CD}    = $socket;
     $hash->{FD}    = $socket->fileno();


wiki only mentions this channel for patch submission so here we are.

ruff

Sorry just realized it wasn't the latest version of the patch, here's the latest (and tested) one:

Index: FHEM/30_HUEBridge.pm
===================================================================
--- FHEM/30_HUEBridge.pm        (Revision 25044)
+++ FHEM/30_HUEBridge.pm        (Arbeitskopie)
@@ -371,6 +371,44 @@
   HUEBridge_closeWebsocket($hash);

   my ($host,undef) = split(':',$hash->{host},2);
+
+  if( my $proxy = AttrVal("global", "proxy", undef) ) {
+    my $h = { %$hash };
+    $h->{url} = "http://$host:$hash->{websocketport}/";
+    $h->{noConn2} = 1;
+    $h->{callback} = sub($) {
+      my ($h) = @_;
+      $h->{conn}->blocking(1);
+      my $pw = AttrVal("global", "proxyAuth", "");                           
+      $pw = "Proxy-Authorization: Basic $pw\r\n" if($pw);                     
+      my $hdr = "CONNECT $host:$hash->{websocketport} HTTP/1.0\r\nUser-Agent: fhem\r\n$pw\r\n";                               
+      syswrite $h->{conn}, $hdr;
+      my $buf;
+      my $len = sysread($h->{conn},$buf,65536);
+      if(!defined($len) || $len <= 0 || $buf !~ m/HTTP.*200/) {
+        HttpUtils_Close($h);
+        Log3 $name, 2, "$name: failed to open websocket via proxy";
+        return;
+      }
+
+      $hash->{CD} = $h->{conn};
+      $hash->{PORT} = $hash->{websocketport};
+      my $ret = "GET ws://$host:$hash->{websocketport} HTTP/1.1\r\n";
+      $ret .= HUEBridge_hash2header( {                  'Host' => "$host:$hash->{websocketport}",
+                                                     'Upgrade' => 'websocket',
+                                                  'Connection' => 'Upgrade',
+                                                      'Pragma' => 'no-cache',
+                                               'Cache-Control' => 'no-cache',
+                                           'Sec-WebSocket-Key' => 'RkhFTQ==',
+                                       'Sec-WebSocket-Version' => '13',
+                                     } );
+      $ret .= "\r\n";
+      syswrite($hash->{CD}, $ret );
+    };
+    HttpUtils_Connect($h);
+    return;
+  }
+
   if( my $socket = IO::Socket::INET->new(PeerAddr=>"$host:$hash->{websocketport}", Timeout=>2, Blocking=>1, ReuseAddr=>1) ) {
     $hash->{CD}    = $socket;
     $hash->{FD}    = $socket->fileno();