SSH local port forwarding using libssh
Asked Answered
D

1

6

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?

Dup answered 13/4, 2017 at 18:5 Comment(2)
@Olaf: This is not about C++ code it is about a functionality provided by a C library that contains the whole logic and is widly known and a C++ wrapper which is basically nothing like int mycppfunc(){return mycfunc()}. Since libssh is one of only two reliable libraries for SSH written in C I think a lot of C programmers are familiar with it. I see no reason to remove the C tag.Dup
Just using a C library from C++ code does not justifythe C tag. Otherwise pretty much every questrion will be a cnadidate for this tag, because all code eventually uses a C function - including Java and Python. Actually it can be doubted the C++ tag is valid either. But as you use that language, I'll leve it - in dubio pro reo.Sacroiliac
K
3

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.

Kempis answered 24/4, 2017 at 1:41 Comment(3)
Thank's for your answer so far. I really tried to find a solution that doesn't need to create sockets manually but it seems like there is none. I wanted to avoid the password prompt that is used by all common ssh implementations therefore I want to avoid the usage of fork and exec. I know I could manage a workaround using tty directly but libssh seemed to be more elegant. It would be great if you could find the time to add a small snippet to clarify your intention of using socket fd and ssh_channel in a vector.Dup
@joseph can you explain in case I use different Networking library like POCO, or wxWidgets? In that case I don't have fd. Also you can add pseudo-code if you don't have time to wrote real code and it will help a lotAustenite
Here is the code sample that do all the stuff: #18812281Healing

© 2022 - 2024 — McMap. All rights reserved.