【问题标题】:Tunnel between servers that supports any port支持任何端口的服务器之间的隧道
【发布时间】:2014-08-04 00:35:45
【问题描述】:

我正在尝试使用 Node.js 创建一个隧道,它允许我从服务器 Y 访问服务器 X。服务器 X 连接到未转发端口的路由器,我不知道服务器的 IP X 直到它连接,这意味着服务器 X 必须打开一个到服务器 Y 的套接字,而不是相反。

我已经使用socket.io 成功创建了这个版本。服务器 X 向服务器 Y 打开一个套接字,然后用户可以在 Web 浏览器中访问服务器 Y,服务器 Y 将请求通过套接字代理到服务器 X。

我想做的是允许访问服务器 X 上的任何类型的端口,并且不仅转发 Web 请求,还转发任何类型的请求。例如,我想允许 SSH 转发,这样我就可以通过服务器 Y 访问服务器 X 上的 SSH(不必是端口 22)。 localtunnel.me 是一个现有的服务,它是我想要实现的一个确切的例子。

是否有任何库可以帮助我实现这一目标,或者我可以很容易地从头开始构建它?我很容易地建立了网络请求隧道,也许它不仅可以支持网络流量吗?我已将代码附加到下面的现有网络隧道中。

服务器 X(连接到服务器 Y 的 3001 端口,接收数据请求并发回:

var socket = require('socket.io-client')('http://localhost:3001');
        socket.on('connect', function(){
            console.log('Connected');

            // Register the event for request of data
            socket.on('request', function(data){
                // Get the path
                var options = {
                  host: 'localhost',
                  port: 3000,
                  path: data.path,
                  method: data.method
                };

                var request = http.get(options, function(resp){
                  resp.on('data', function(chunk){
                    socket.emit('response', { html: chunk });
                    // Probably need to fix this for file transfers. Use resp.on('end'
                  });
                }).on("error", function(e){
                  console.log("Got error: " + e.message);
                });

                //Write our post data to the request
                request.write(data.data);
                //End the request.
                request.end();

            });
            socket.on('disconnect', function(){});
        });

服务器 Y(监听 3001 端口连接到服务器 X,并监听端口 3002 以获取来自网络浏览器中用户的请求以转发到服务器 X:

app.listen(3001);

var rwPortalSocket;

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
    // Save the socket object
    rwPortalSocket = socket;
});

console.log('Data channel server running at http://127.0.0.1:3001/');

// Create web server
var http = require('http');
var qs = require('querystring');

http.createServer(function (req, res) {
    // Send a request
    rwPortalSocket.emit('request', { path: req.url, method: req.method });

    // When we get a response
    rwPortalSocket.on('response', function (responseData) {
        res.writeHead(200);
        res.end(responseData.html);
    });

}).listen(3002, '127.0.0.1');

console.log('Web server running at http://127.0.0.1:3002/');

编辑

我现在更新了我的代码,以便支持任何 TCP 端口或数据包类型。当我告诉 net.connect 连接到 Web 服务器时,代码工作正常,但是当我告诉它连接 SSH 服务器时,我的 SSH 客户端抱怨 Protocol error: expected packet type 31, got 20

我在下面添加了一个连接到 SSH 服务器的新代码示例。

服务器 X(连接到服务器 Y 的 3001 端口,接收数据请求并发回:

var socket = require('socket.io-client')('http://localhost:3001');
        socket.on('connect', function(){
            console.log('Connected');

            // Connect to 22
            var buff = "";
            var connected = false;
            var net = require('net');
            var client = net.connect({host: 'myserver.com', port: 22}, function() { //'connect' listener
                connected = true; 
                console.log('Connected to 22');
            });

            // Register the event for request of data
            socket.on('request', function(data){

                if (!connected)
                {
                    client = net.connect({host: 'myserver.com', port: 22}, function() { //'connect' listener
                        connected = true; 
                        console.log('Connected to 22');
                         client.write(data.data);
                    });
                }
                else
                {
                    client.write(data.data);
                }

                client.setMaxListeners(0); 
                // When data comes back to this service, we send it on to the other server
                client.on('data', function(data) {
                  //console.log(data.toString());
                    console.log('Server sent back: ' + data.toString());
                    if (connected)
                    {
                        socket.emit('response', { data: data });
                    } else {
                       buff += d.toString();
                    }
                });
                client.on('end', function() {
                  console.log('Disconnected from 22');
                    connected = false;
                });

                client.on('error', function(e) {
                    console.log(e);
                });

                console.log('Client sent: ' + data.data);
            });
            socket.on('disconnect', function(){});
        });

服务器 Y(监听 3001 端口连接服务器 X,监听 3002 端口 SSH 客户端(终端)中的用户请求转发到服务器 X:

app.listen(3001);

var rwPortalSocket;

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
    // Save the socket object
    rwPortalSocket = socket;
});

console.log('Data channel server running at http://127.0.0.1:3001/');

// Listen for tunnel requests
net = require('net');

var server = net.createServer(function(s) { //'connection' listener
  s.on('end', function() {
      console.log('server disconnected');
  });

  s.on('data', function (d) {
    rwPortalSocket.emit('request', { data: d });
  });

    s.on('error', function(e) {
        console.log(e);
    });

    s.setMaxListeners(0); 

    // When we get a response
    rwPortalSocket.on('response', function (d) {
        s.write(d.data);
    });

});

server.listen(3002, function() { //'listening' listener
  console.log('server bound');
});

console.log('Web server running at http://127.0.0.1:3002/');

【问题讨论】:

    标签: node.js socket.io tunnel


    【解决方案1】:

    如果到服务器 X 的“连接”都是基于 TCP 的,那么您可以在两端运行 SSH 服务器。然后,服务器 X 将连接到服务器 Y,仅转发服务器 Y 上的某个端口,该端口将指向服务器 X 上运行的 SSH 服务器。然后您可以使用像 ssh2 这样的节点模块连接到服务器 X 上的任何端口。

    动态转发的另一个选项是设置一个 socks 代理,如this answer 中所述。从那里您可以使用来自 npm 的 socks 客户端模块,例如 socks5-client

    如果您有一组固定的可用端口,您可以通过在服务器 Y 和服务器 X 上连接一个 SSH 服务器并为您希望可用的每个端口创建一个端口转发来简化上述解决方案.

    Here 是另一个选项的示例:通过 SSH 从服务器 Y 连接到服务器 X,并使用 SSH 连接(通过 ssh2)打开服务器 X 上的连接。

    【讨论】:

    • 感谢您的建议。我认为您的建议将是一个很好的解决方案,创建一个连接到服务器 X 上的 SSH 服务器的路径,然后您可以从那里访问任何端口。我不能允许服务器 Y 上的 SSH 访问,因为服务器 X 不安全,所以我不能在那里拥有服务器 Y 的 SSH 登录凭据。我正在尝试创建一个到 SSH 的隧道,并且我已经更新了我的代码以允许任何类型的 TCP 转发(或者应该是)。我的 SSH 客户端不断报告“协议错误:预期的数据包类型 31,得到 20”。有任何想法吗?我更新的代码在上面的编辑部分。
    • 那么服务器 X 有一个运行的 SSH 服务器可以从服务器 Y 访问?
    • 假设服务器 X 确实有一个 SSH 服务器正在运行,here 是一个使用 ssh2 模块进行端口转发的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-28
    • 1970-01-01
    • 2014-01-18
    相关资源
    最近更新 更多