Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SSH local port forwarding using libssh

Problem

I try to do local port forwarding using libssh with the libssh-C++-wrapper. My intention is to forward port localhost:3306 on a server to localhost:3307 on 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;
}

with session in the constructor of ssh::Channel being of type ssh::Session.

The code above prints Channel is open!. If I try to connect to localhost:3307 using the MySQL Connector/C++ I get

ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (61)

Observations

  • If I use the shell command $ ssh -L 3307:localhost:3306 [email protected] everything works fine and I can connect.
  • If I use ssh::Session session used in the constructor or ssh::Channel channel to execute remote shell commands everything works therefore the session is fine!
  • The documentation of libssh (which is total crap for the C++ wrapper libsshpp.hpp since a lot of public member functions are not documented and you have to look into the source code) shows that ssh::Channel::openForward() is a wrapper for the C function ssh_channel_open_forward()
  • The documentation of ssh_channel_open_forward() states

    Warning
    This function does not bind the local port and does not automatically forward the content of a socket to the channel. You still have to use channel_read and channel_write for this.

I think that could cause the problem. I have no problem by reading and writing in to the ssh:Channel but thats not how the MySQL Connector/C++ works.

Question

How can I achieve the same behaviour produced by the common shell command

$ ssh -L 3307:localhost:3306 [email protected]

using libssh?

like image 773
Orzowei Avatar asked Oct 17 '22 14:10

Orzowei


1 Answers

Warning

This function does not bind the local port and does not automatically forward the content of a socket to the channel. You still have to use channel_read and channel_write for this.

This is telling you that you need to write your own local socket code. Unfortunately, it doesn't do it for you.

The simplest implementation would be to bind a local socket, and use ssh_select to listen for events (e.g. new connection to accept, socket or channel events). You can keep your socket fds aand ssh_channels in a vector for easy management.

When you get any event, just loop over all the operations in a non-blocking way, i.e.

  • try to accept a new connection, and append the fd, and a new ssh_channel (created as in your question) to your vectors.
  • try to read all the socket fds, and forward anything to the corresponding ssh channel using ssh_channel_write (make sure to setsockopt SO_RCVTIMEO to 0)
  • try to read all the channels, using ssh_channel_read_nonblocking, and forward to the socket fd using write.

You also need to handle errors everywhere, and close the corresponding fd and ssh_channel.

Overall it's probably going to be too much code for a StackOverflow answer, but I may come back and add it in if I get time.

The tempting alternative to all that would be to just run ssh -L ... as a subprocess using fork & exec, avoiding all that boilerplate socket code, and benefitting from an efficient, bug-free implementation.

like image 117
Joseph Ireland Avatar answered Oct 21 '22 08:10

Joseph Ireland