@@ -3043,6 +3043,12 @@ class SSHClientConnection(SSHConnection):
3043
3043
UNIX domain socket forwarding can be set up by calling
3044
3044
:meth:`forward_local_path` or :meth:`forward_remote_path`.
3045
3045
3046
+ Mixed forwarding from a TCP port to a UNIX domain socket or
3047
+ vice-versa can be set up by calling :meth:`forward_local_port_to_path`,
3048
+ :meth:`forward_local_path_to_port`,
3049
+ :meth:`forward_remote_port_to_path`, or
3050
+ :meth:`forward_remote_path_to_port`.
3051
+
3046
3052
"""
3047
3053
3048
3054
_options : 'SSHClientConnectionOptions'
@@ -4646,6 +4652,113 @@ async def listen_reverse_ssh(self, host: str = '',
4646
4652
return await listen_reverse (host , port , tunnel = self ,
4647
4653
** kwargs ) # type: ignore
4648
4654
4655
+ @async_context_manager
4656
+ async def forward_local_port_to_path (self , listen_host : str ,
4657
+ listen_port : int ,
4658
+ dest_path : str ) -> SSHListener :
4659
+ """Set up local TCP port forwarding to a remote UNIX domain socket
4660
+
4661
+ This method is a coroutine which attempts to set up port
4662
+ forwarding from a local TCP listening port to a remote UNIX
4663
+ domain path via the SSH connection. If the request is successful,
4664
+ the return value is an :class:`SSHListener` object which can be
4665
+ used later to shut down the port forwarding.
4666
+
4667
+ :param listen_host:
4668
+ The hostname or address on the local host to listen on
4669
+ :param listen_port:
4670
+ The port number on the local host to listen on
4671
+ :param dest_path:
4672
+ The path on the remote host to forward the connections to
4673
+ :type listen_host: `str`
4674
+ :type listen_port: `int`
4675
+ :type dest_path: `str`
4676
+
4677
+ :returns: :class:`SSHListener`
4678
+
4679
+ :raises: :exc:`OSError` if the listener can't be opened
4680
+
4681
+ """
4682
+
4683
+ async def tunnel_connection (
4684
+ session_factory : SSHUNIXSessionFactory [bytes ],
4685
+ _orig_host : str , _orig_port : int ) -> \
4686
+ Tuple [SSHUNIXChannel [bytes ], SSHUNIXSession [bytes ]]:
4687
+ """Forward a local connection over SSH"""
4688
+
4689
+ return (await self .create_unix_connection (session_factory ,
4690
+ dest_path ))
4691
+
4692
+ self .logger .info ('Creating local TCP forwarder from %s to %s' ,
4693
+ (listen_host , listen_port ), dest_path )
4694
+
4695
+ try :
4696
+ listener = await create_tcp_forward_listener (self , self ._loop ,
4697
+ tunnel_connection ,
4698
+ listen_host ,
4699
+ listen_port )
4700
+ except OSError as exc :
4701
+ self .logger .debug1 ('Failed to create local TCP listener: %s' , exc )
4702
+ raise
4703
+
4704
+ if listen_port == 0 :
4705
+ listen_port = listener .get_port ()
4706
+
4707
+ self ._local_listeners [listen_host , listen_port ] = listener
4708
+
4709
+ return listener
4710
+
4711
+ @async_context_manager
4712
+ async def forward_local_path_to_port (self , listen_path : str ,
4713
+ dest_host : str ,
4714
+ dest_port : int ) -> SSHListener :
4715
+ """Set up local UNIX domain socket forwarding to a remote TCP port
4716
+
4717
+ This method is a coroutine which attempts to set up UNIX domain
4718
+ socket forwarding from a local listening path to a remote host
4719
+ and port via the SSH connection. If the request is successful,
4720
+ the return value is an :class:`SSHListener` object which can
4721
+ be used later to shut down the UNIX domain socket forwarding.
4722
+
4723
+ :param listen_path:
4724
+ The path on the local host to listen on
4725
+ :param dest_host:
4726
+ The hostname or address to forward the connections to
4727
+ :param dest_port:
4728
+ The port number to forward the connections to
4729
+ :type listen_path: `str`
4730
+ :type dest_host: `str`
4731
+ :type dest_port: `int`
4732
+
4733
+ :returns: :class:`SSHListener`
4734
+
4735
+ :raises: :exc:`OSError` if the listener can't be opened
4736
+
4737
+ """
4738
+
4739
+ async def tunnel_connection (
4740
+ session_factory : SSHTCPSessionFactory [bytes ]) -> \
4741
+ Tuple [SSHTCPChannel [bytes ], SSHTCPSession [bytes ]]:
4742
+ """Forward a local connection over SSH"""
4743
+
4744
+ return await self .create_connection (session_factory , dest_host ,
4745
+ dest_port , '' , 0 )
4746
+
4747
+ self .logger .info ('Creating local UNIX forwarder from %s to %s' ,
4748
+ listen_path , (dest_host , dest_port ))
4749
+
4750
+ try :
4751
+ listener = await create_unix_forward_listener (self , self ._loop ,
4752
+ tunnel_connection ,
4753
+ listen_path )
4754
+ except OSError as exc :
4755
+ self .logger .debug1 ('Failed to create local UNIX listener: %s' , exc )
4756
+ raise
4757
+
4758
+ self ._local_listeners [listen_path ] = listener
4759
+
4760
+ return listener
4761
+
4649
4762
@async_context_manager
4650
4763
async def forward_remote_port (self , listen_host : str ,
4651
4764
listen_port : int , dest_host : str ,
@@ -4727,6 +4840,88 @@ def session_factory() -> Awaitable[SSHUNIXSession[bytes]]:
4727
4840
4728
4841
return await self .create_unix_server (session_factory , listen_path )
4729
4842
4843
+ @async_context_manager
4844
+ async def forward_remote_port_to_path (self , listen_host : str ,
4845
+ listen_port : int ,
4846
+ dest_path : str ) -> SSHListener :
4847
+ """Set up remote TCP port forwarding to a local UNIX domain socket
4848
+
4849
+ This method is a coroutine which attempts to set up port
4850
+ forwarding from a remote TCP listening port to a local UNIX
4851
+ domain socket path via the SSH connection. If the request is
4852
+ successful, the return value is an :class:`SSHListener` object
4853
+ which can be used later to shut down the port forwarding. If
4854
+ the request fails, `None` is returned.
4855
+
4856
+ :param listen_host:
4857
+ The hostname or address on the remote host to listen on
4858
+ :param listen_port:
4859
+ The port number on the remote host to listen on
4860
+ :param dest_path:
4861
+ The path on the local host to forward connections to
4862
+ :type listen_host: `str`
4863
+ :type listen_port: `int`
4864
+ :type dest_path: `str`
4865
+
4866
+ :returns: :class:`SSHListener`
4867
+
4868
+ :raises: :class:`ChannelListenError` if the listener can't be opened
4869
+
4870
+ """
4871
+
4872
+ def session_factory (_orig_host : str ,
4873
+ _orig_port : int ) -> Awaitable [SSHUNIXSession ]:
4874
+ """Return an SSHTCPSession used to do remote port forwarding"""
4875
+
4876
+ return cast (Awaitable [SSHUNIXSession ],
4877
+ self .forward_unix_connection (dest_path ))
4878
+
4879
+ self .logger .info ('Creating remote TCP forwarder from %s to %s' ,
4880
+ (listen_host , listen_port ), dest_path )
4881
+
4882
+ return await self .create_server (session_factory , listen_host ,
4883
+ listen_port )
4884
+
4885
+ @async_context_manager
4886
+ async def forward_remote_path_to_port (self , listen_path : str ,
4887
+ dest_host : str ,
4888
+ dest_port : int ) -> SSHListener :
4889
+ """Set up remote UNIX domain socket forwarding to a local TCP port
4890
+
4891
+ This method is a coroutine which attempts to set up UNIX domain
4892
+ socket forwarding from a remote listening path to a local TCP
4893
+ host and port via the SSH connection. If the request is
4894
+ successful, the return value is an :class:`SSHListener` object
4895
+ which can be used later to shut down the port forwarding. If
4896
+ the request fails, `None` is returned.
4897
+
4898
+ :param listen_path:
4899
+ The path on the remote host to listen on
4900
+ :param dest_host:
4901
+ The hostname or address to forward connections to
4902
+ :param dest_port:
4903
+ The port number to forward connections to
4904
+ :type listen_path: `str`
4905
+ :type dest_host: `str`
4906
+ :type dest_port: `int`
4907
+
4908
+ :returns: :class:`SSHListener`
4909
+
4910
+ :raises: :class:`ChannelListenError` if the listener can't be opened
4911
+
4912
+ """
4913
+
4914
+ def session_factory () -> Awaitable [SSHTCPSession [bytes ]]:
4915
+ """Return an SSHUNIXSession used to do remote path forwarding"""
4916
+
4917
+ return cast (Awaitable [SSHTCPSession [bytes ]],
4918
+ self .forward_connection (dest_host , dest_port ))
4919
+
4920
+ self .logger .info ('Creating remote UNIX forwarder from %s to %s' ,
4921
+ listen_path , (dest_host , dest_port ))
4922
+
4923
+ return await self .create_unix_server (session_factory , listen_path )
4924
+
4730
4925
@async_context_manager
4731
4926
async def forward_socks (self , listen_host : str ,
4732
4927
listen_port : int ) -> SSHListener :
0 commit comments