Rust中

时间:2016-04-26 12:58:14

标签: http ssh tcp rust tunnel

我试图在Rust中编写一个小程序来基本完成ssh -L 5000:localhost:8080的工作:在我的机器上的localhost:5000和远程机器上的localhost:8080之间建立隧道,因此,如果HTTP服务器在远程端口8080上运行,我可以通过localhost:5000在本地访问它,绕过远程防火墙,这可能会阻止对8080的外部访问。

我意识到ssh已经完成了这个并且可靠,这是一个学习项目,如果我让它工作,我可能会添加一些功能:)这是一个准系统(没有线程,没有错误处理)我到目前为止所提出的版本(应该在Rust 1.8上编译):

extern crate ssh2; // see http://alexcrichton.com/ssh2-rs/

use std::io::Read;
use std::io::Write;
use std::str;
use std::net;

fn main() {
  // establish SSH session with remote host
  println!("Connecting to host...");
  // substitute appropriate value for IPv4
  let tcp = net::TcpStream::connect("<IPv4>:22").unwrap();
  let mut session = ssh2::Session::new().unwrap();
  session.handshake(&tcp).unwrap();
  // substitute appropriate values for username and password
  // session.userauth_password("<username>", "<password>").unwrap();
  assert!(session.authenticated());
  println!("SSH session authenticated.");

  // start listening for TCP connections
  let listener = net::TcpListener::bind("localhost:5000").unwrap();
  println!("Started listening, ready to accept");
  for stream in listener.incoming() {
    println!("===============================================================================");

    // read the incoming request
    let mut stream = stream.unwrap();
    let mut request = vec![0; 8192];
    let read_bytes = stream.read(&mut request).unwrap();
    println!("REQUEST ({} BYTES):\n{}", read_bytes, str::from_utf8(&request).unwrap());

    // send the incoming request over ssh on to the remote localhost and port
    // where an HTTP server is listening
    let mut channel = session.channel_direct_tcpip("localhost", 8080, None).unwrap();
    channel.write(&request).unwrap();

    // read the remote server's response (all of it, for simplicity's sake)
    // and forward it to the local TCP connection's stream
    let mut response = Vec::new();
    let read_bytes = channel.read_to_end(&mut response).unwrap();
    stream.write(&response).unwrap();
    println!("SENT {} BYTES AS RESPONSE", read_bytes);
  };
}

事实证明,这种作品,但并不完全。例如。如果在远程服务器上运行的应用程序是Cloud9 IDE Core/SDK,则会加载主HTML页面和一些资源,但请求其他资源.js,{{1 }})系统地返回空(无论是主页还是直接请求),即.css 的调用中没有读取任何内容。其他(更简单?)网络应用程序或静态网站似乎工作正常。至关重要的是,使用channel.read_to_end()时,即使是Cloud9 Core也能正常工作。

我预计其他更复杂的应用也会受到影响。我在代码中看到了潜在的潜在区域:

  1. Rust的流读/写API:对ssh -L 5000:localhost:8080的调用可能与我想的不同,只是偶然为某些请求做了正确的事情?
  2. HTTP:也许我需要在将请求转发到远程服务器之前修改HTTP头?或者我可以通过拨打channel.read_to_end()
  3. 尽快放弃响应流
  4. Rust本身 - 这是我第一次相对认真地学习系统编程语言
  5. 我已经尝试过使用上述某些内容,但我会感谢任何探索路径的建议,最好还要解释为什么可能会出现问题:)

1 个答案:

答案 0 :(得分:1)

tl; dr :使用Go及其网络库执行此特定任务

原来我对HTTP的工作方式的基本理解可能是错误的(我最初认为我可以通过ssh连接来回挖掘数据,但我无法实现这一点 - 如果有人知道的话为了做到这一点,我仍然很好奇!)。请参阅评论中的一些建议,但基本上归结为HTTP连接如何启动,保持活动和关闭的复杂性。我尝试使用hyper包来抽象掉这些细节,但事实证明ssh2包(就像底层的libssh2)不是线程安全的,这使得在超级处理程序中使用ssh Session成为不可能。

此时,我认为初学者没有简单高级方式来实现Rust中的这种方式(我是我必须首先做一些低级别的管道,因为我不能可靠地和惯用地做到这一点,我认为它根本不值得做。所以我最终分配了用Go编写的这个SSHTunnel存储库,其中对此特定任务的库支持很容易获得,我可以找到我在OP中描述的Cloud9设置的解决方案here