SSH local port redirection using libssh
Problem
I am trying to do local port forwarding using libssh with libssh-C ++ wrapper . My intention is to forward the port localhost:3306
to a server on localhost:3307
my machine via SSH to connect via MySQL to localhost:3307
.
void ssh_session::forward(){
ssh::Channel channel(this->session);
//remotehost, remoteport, localhost, localport
channel.openForward("localhost",3306,"localhost",3307);
std::cout<< "Channel is " << (channel.isOpen()?"open!":"closed!") << std::endl;
}
c session
in the constructor ssh::Channel
is of type ssh::Session
.
The above code prints Channel is open!
. If I try to connect to localhost:3307
using MySQL Connector / C ++ I get
ERROR 2003 (HY000): Cannot connect to MySQL server at "127.0.0.1" (61)
Observations
- If I use the shell command
$ ssh -L 3307:localhost:3306 me@myserver.com
everything works fine and I can connect. - If I use
ssh::Session session
, used in constructor orssh::Channel channel
to execute remote shell commands everything works, so the session is fine! - The libssh documentation (which is complete crap for a C ++ wrapper
libsshpp.hpp
since a lot of public member functions are not documented and you should look into the source code) shows whatssh::Channel::openForward()
is a C wrapper functionssh_channel_open_forward()
- documentation from
ssh_channel_open_forward()
statesCaution
This function does not bind a local port and does not redirect socket contents to a pipe. To do this, you still need to use channel_read and channel_write.
I think this might be causing the problem. I have no problem reading and writing to ssh:Channel
, but that's not how MySQL Connector / C ++ works.
Question
How can I achieve the same behavior produced by a common shell command
$ ssh -L 3307:localhost:3306 me@myserver.com
using libssh?
source to share
Warning
This function does not bind the local port and does not redirect the contents of the socket to the pipe. You still need to use channel_read and channel_write for this.
This tells you that you need to write your own local socket code. Unfortunately, it doesn't do it for you.
The simplest implementation would be a bind
local socket and use ssh_select
to listen for events (like a new event connection accept
, socket, or pipe). You can store your fd
aand socket ssh_channel
in a vector for easy handling.
When you receive any event, just iterate over all operations in a non-blocking way, i.e.
- try a
accept
new connection and add fd and a new ssh_channel (created as in your question) to your vectors. - try
read
all socketfd
s and move anything to the appropriate ssh pipe usingssh_channel_write
(make sure setockopt SO_RCVTIMEO is 0) - try to read all channels using
ssh_channel_read_nonblocking
, and go to the socketfd
withwrite
.
You also need to handle errors everywhere and close the corresponding fd and ssh_channel.
All in all it will probably be too much code for a StackOverflow answer, but I can go back and add it if I get the time.
A tempting alternative to anything that would be just to run ssh -L ...
as a subprocess using fork
and exec
, avoiding all that template socket code and benefiting from an efficient, bug-free implementation.
source to share