2016-04-26 16 views
6

Tôi đang cố gắng viết một chương trình nhỏ bằng Rust để hoàn thành về cơ bản những gì ssh -L 5000:localhost:8080 làm: thiết lập đường hầm giữa localhost:5000 trên máy của tôi và localhost:8080 trên máy từ xa để máy chủ HTTP đang chạy trên cổng 8080 trên điều khiển từ xa, tôi có thể truy cập vào cổng cục bộ của mình qua localhost:5000, bỏ qua tường lửa của từ xa có thể chặn truy cập bên ngoài đến 8080.Đường hầm TCP qua SSH trong Rust

Tôi nhận thấy chính xác và đáng tin cậy, đây là một dự án học tập, cộng với tôi có thể thêm một số chức năng nếu tôi nhận được nó để làm việc :) Đây là một barebones (không có luồng, không xử lý lỗi) phiên bản của những gì tôi đã đưa ra cho đến nay (nên biên dịch trên 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); 
    }; 
} 

Khi nó quay ra, loại tác phẩm này, nhưng không hoàn toàn. Ví dụ. nếu ứng dụng chạy trên máy chủ từ xa là Cloud9 IDE Core/SDK, trang HTML chính được nạp và một số tài nguyên là tốt, nhưng yêu cầu cho các nguồn lực khác (.js, .css) có hệ thống trở lại trống (cho dù là theo yêu cầu của trang chính hoặc trực tiếp), tức là không có gì được đọc trong cuộc gọi đến channel.read_to_end(). Các ứng dụng web khác (đơn giản hơn?) Hoặc các trang tĩnh dường như hoạt động tốt. Quan trọng, khi sử dụng ssh -L 5000:localhost:8080, ngay cả Cloud9 Core hoạt động tốt.

Tôi hy vọng rằng các ứng dụng phức tạp hơn khác cũng sẽ bị ảnh hưởng. Tôi thấy lĩnh vực tiềm năng khác nhau nơi mà các lỗi có thể được ẩn giấu trong mã của tôi:

  1. Rust dòng đọc/ghi API: maybe cuộc gọi đến channel.read_to_end() hoạt động khác nhau hơn tôi suy nghĩ và chỉ vô tình làm điều đúng đắn đối với một số loại yêu cầu ?
  2. HTTP: có thể tôi cần phải nhắn tin với tiêu đề HTTP trước khi chuyển tiếp yêu cầu tới máy chủ từ xa? hoặc có thể tôi sẽ sớm từ bỏ luồng phản hồi bằng cách chỉ gọi channel.read_to_end()?
  3. Rust bản thân - đó là nỗ lực tương đối nghiêm túc đầu tiên của tôi tại học một ngôn ngữ lập trình hệ thống

Tôi đã cố gắng chơi với một số những điều trên, nhưng tôi sẽ đánh giá cao bất cứ đề nghị các đường dẫn để khám phá, tốt nhất cùng với một lời giải thích là tại sao mà có thể là vấn đề :)

+0

Cảm ơn, bạn nói đúng, hãy tập trung tốt hơn :) – dlukes

+0

Tôi gặp sự cố khi nhận mã để chạy cục bộ, nhưng 'vec! [0; 8192] 'là đáng ngờ. Điều này tạo ra một vectơ với nhiều số không. nếu bạn đọc một byte từ socket, vẫn sẽ có 8191 số 0 theo sau nó trong bộ đệm. Khi bạn viết nó ra, bạn đang viết tất cả những số 0, mà phía bên phải xử lý. Bạn chỉ nên gửi dữ liệu bạn đã đọc. – Shepmaster

+0

Ngoài ra, bạn có thể cần đọc đầu vào cho đến khi tiêu đề HTTP hoàn tất ('\ r \ n \ r \ n', IIRC), sau đó gửi luồng ngược dòng đó. – Shepmaster

Trả lời

2

tl; dr: sử dụng Go và thư viện mạng của mình cho nhiệm vụ đặc biệt này

Hóa ra sự hiểu biết rất thô sơ của tôi về cách thức HTTP làm việc có thể có lỗi ở đây (tôi Ban đầu tôi nghĩ tôi có thể xẻ dữ liệu qua lại kết nối ssh, nhưng tôi đã không thể đạt được điều đó - nếu ai đó biết cách để làm điều này, tôi vẫn tò mò!). Xem một số gợi ý trong các nhận xét, nhưng về cơ bản nó tóm lại những phức tạp về cách các kết nối HTTP được khởi tạo, giữ cho sống và đóng lại. Tôi đã thử sử dụng thùng hyper để trừu tượng hóa các chi tiết này, nhưng hóa ra là thùng ssh2 (giống như libssh2 cơ bản) không phải là luồng an toàn, khiến cho nó không thể sử dụng phiên ssh trong trình xử lý siêu.

Tại thời điểm này, tôi quyết định không có đơn giản, cao cấp đường cho một người mới bắt đầu để đạt được điều này trong Rust (Tôi sẽ phải làm một số đường ống dẫn nước ở mức độ thấp đầu tiên, và kể từ khi tôi có thể 'làm điều đó một cách đáng tin cậy và thành ngữ, tôi nghĩ rằng nó không đáng làm gì cả).Vì vậy, tôi đã kết thúc kho lưu trữ SSHTunnel được viết ở Go, nơi hỗ trợ thư viện cho tác vụ cụ thể này sẵn có và giải pháp của tôi cho thiết lập Cloud9 được mô tả trong OP có thể được tìm thấy here.

Các vấn đề liên quan