I must admit this is a use case I never considered when building the AsyncSSH API for forwarding. I’m not even sure at the time that I knew OpenSSH was capable of crossing over like this.
While there’s no built-in function to do this today, the building blocks are there. You’d basically start from something like the forward_local_port() function but replace the tunnel_connection() method defined inside it with something more like the tunnel_connection() from the forward_local_path() function.
Unfortunately, the current implementation relies on a create_tcp_forward_listener() function which is not public in AsyncSSH, and that in turn brings in some other local classes like SSHForwardListener. So, it might be difficult to do this from outside without an internal import.
More specifically, the following seems to work:
import asyncio, asyncssh
from asyncssh.listener import create_tcp_forward_listener
async def cross_forward(conn, listen_host, listen_port, dest_path):
async def tunnel_connection(session_factory, orig_host, orig_port):
return await conn.create_unix_connection(session_factory, dest_path)
loop = asyncio.get_event_loop()
listener = await create_tcp_forward_listener(conn, loop, tunnel_connection,
listen_host, listen_port)
if listen_port == 0:
listen_port = listener.get_port()
return listener
async def run_client() -> None:
async with asyncssh.connect('localhost') as conn:
listener = await cross_forward(conn, '', 8080, '/tmp/socket')
await listener.wait_closed()
asyncio.run(run_client())
I’ll have to think about what kind of naming to use if I did want to cover the two crossover cases. I suppose it could be something like forward_local_port_to_path() and forward_local_path_to_port(), with appropriate adjustments in the arguments to each. I’m not sure how common a use case this is, but if I can do it without much disruption, I’d consider adding it.
Can you try out the above and let me know if it works for you?
--