使用libssh进行SSH本地端口转发

6

问题

我尝试使用libsshlibssh-C++-wrapper进行本地端口转发。我的意图是通过SSH将服务器上的端口localhost:3306转发到我的机器上的localhost:3307,以便通过MySQL连接到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;
}

使用ssh::Channel构造函数中的session类型应为ssh::Session。以上代码将打印Channel is open!。如果我尝试使用MySQL Connector/C++连接到localhost:3307,我会得到以下错误:

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

观察结果

  • 如果我使用shell命令$ ssh -L 3307:localhost:3306 me@myserver.com,一切正常,我可以连接。
  • 如果我在构造函数中使用ssh::Session sessionssh::Channel channel执行远程shell命令,则一切正常,因此会话没问题!
  • libssh的文档(对于C++包装器libsshpp.hpp来说是一团糟,因为许多公共成员函数没有记录,必须查看源代码)显示ssh::Channel::openForward()是C函数ssh_channel_open_forward()的包装器
  • ssh_channel_open_forward()的文档说明:

    警告
    此函数不绑定本地端口,也不自动将套接字内容转发到通道。 您仍然需要使用channel_read和channel_write进行操作。

我认为这可能是问题的原因。 我在ssh:Channel中读写没有问题,但MySQL Connector/C++无法正常工作。

问题

如何实现与常见shell命令产生相同的行为?

$ ssh -L 3307:localhost:3306 me@myserver.com

使用libssh吗?


@Olaf:这不是关于C++代码的问题,而是关于一个包含整个逻辑并被广泛认可的C库提供的功能和基本上与int mycppfunc(){return mycfunc()}完全不同的C++包装器。由于libssh是仅有的两个可靠的用C编写的SSH库之一,我认为很多C程序员都熟悉它。我看不到删除C标签的理由。 - dtell
仅仅在C++代码中使用C库并不足以证明需要使用C标签。否则,几乎每个问题都可以成为此标签的候选项,因为所有代码最终都会使用C函数,包括Java和Python。实际上,C++标签的有效性也可能受到质疑。但是由于您使用了该语言,我会保留它 - 在怀疑时为被告辩护。 - too honest for this site
1个回答

3

警告

此函数不绑定本地端口,也不自动将套接字内容转发到通道。您仍然需要使用channel_read和channel_write来完成这一点。

这告诉您需要编写自己的本地套接字代码。不幸的是,它不会为您执行此操作。

最简单的实现方法是bind本地套接字,并使用ssh_select监听事件(例如,accept新连接、套接字或通道事件)。您可以将套接字fdssh_channel保存在一个向量中,以便进行轻松管理。

当您收到任何事件时,只需以非阻塞方式循环执行所有操作,即:

  • 尝试accept新连接,并将fd和新的ssh_channel(如您的问题中创建的)附加到您的向量中。
  • 尝试读取所有套接字fd,并使用ssh_channel_write将任何内容转发到相应的ssh通道(确保设置setsockopt SO_RCVTIMEO为0)
  • 尝试读取所有通道,使用ssh_channel_read_nonblocking,并使用write将其转发到套接字fd

您还需要处理所有错误,并关闭相应的fd和ssh_channel。

总的来说,这可能会是太多的代码用于StackOverflow答案,但如果我有时间,我可以回来添加它。

与所有那些样板套接字代码相比,更具诱惑性的替代方法是仅使用forkexec作为子进程运行ssh -L ...,避免所有该代码,并从高效、无缺陷的实现中受益。


谢谢你迄今为止的回答。我真的尝试找到一种不需要手动创建套接字的解决方案,但似乎没有。我想避免所有常见的ssh实现使用的密码提示,因此我想避免使用forkexec。我知道我可以直接使用tty来管理一个解决方法,但是libssh似乎更加优雅。如果您能抽出时间添加一个小片段以澄清您在向量中使用套接字fdssh_channel的意图,那将非常棒。 - dtell
@joseph,如果我使用不同的网络库,比如POCO或wxWidgets,你能解释一下吗?在这种情况下,我没有fd。如果你没时间写真正的代码,也可以添加伪代码,这会很有帮助。 - Stefano Mtangoo
这里是一个代码示例,它可以完成所有的工作: https://stackoverflow.com/questions/18811781/c-can-i-forward-port-for-external-application-with-libssh - bam
这是一个完成所有工作的代码示例: https://stackoverflow.com/questions/18811781/c-can-i-forward-port-for-external-application-with-libssh - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接