使反向TCP连接接受任意数量的连接(就像普通的TCP服务器一样)

huangapple go评论151阅读模式
英文:

Make reverse TCP connection accept any amount of connections (like a normal TCP server)

问题

我正在尝试创建一个反向代理到基于CONNECT的HTTP代理。想要使用代理的用户将机器A视为HTTP代理。它的工作方式如下:

  1. 机器B打开一个TCP套接字到机器A
  2. 机器A上,一个TCP套接字在一个端口上暴露,并且所有传入的数据都被隧道传输到机器B(io.Copy)。
  3. 机器B上,所有的数据都被隧道传输到本地的HTTP服务器和与机器A的套接字。

本质上,这是一个在HTTP代理后面的反向代理。之所以这么复杂,是因为HTTP代理位于NAT(在机器B上)后面,因此无法直接访问。使用案例是能够在NAT后面托管一个HTTP代理。

机器A的隧道(Go):

package main

import (
	"log"
	"net"
)

func Conn(c *net.TCPConn) string {
	return c.RemoteAddr().String() + " (" + c.LocalAddr().String() + ")"
}

func ProxifyConns(recipientConn, donorConn *net.TCPConn) {
	log.Println("Proxying", Conn(recipientConn), "and", Conn(donorConn))
	go func() {
		_, err := io.Copy(recipientConn, donorConn)
		if err != nil {
			utils.StacktraceError(err)
		}
		recipientConn.Close()
	}()
	go func() {
		_, err := io.Copy(donorConn, recipientConn)
		if err != nil {
			utils.StacktraceError(err)
		}
		recipientConn.Close()
	}()
}

func main() {
	// 打开捐赠者监听器
	donorsAddr, err := net.ResolveTCPAddr("tcp4", ":11000")
	if err != nil {
		utils.StacktraceErrorAndExit(err)
	}
	listenerDonors, err := net.ListenTCP("tcp", donorsAddr)
	if err != nil {
		utils.StacktraceErrorAndExit(err)
	}
	defer listenerDonors.Close()
	log.Println("Listening for donors on", listenerDonors.Addr())

	// 打开接收者监听器
	recipientsAddr, err := net.ResolveTCPAddr("tcp4", ":10000")
	if err != nil {
		utils.StacktraceErrorAndExit(err)
	}
	listenerRecipients, err := net.ListenTCP("tcp", recipientsAddr)
	if err != nil {
		utils.StacktraceErrorAndExit(err)
	}
	defer listenerRecipients.Close()
	log.Println("Listening for recipients on", listenerRecipients.Addr())

	// 处理捐赠者连接
	donorConns := make(chan *net.TCPConn)
	go func() {
		for {
			donorConn, err := listenerDonors.AcceptTCP()
			donorConn.SetKeepAlive(true)
			if err != nil {
				utils.StacktraceErrorAndExit(err)
				return
			}
			log.Println("New donor connection from", Conn(donorConn))
			donorConns <- donorConn
		}
	}()

	// 处理接收者连接
	for {
		recipientConn, err := listenerRecipients.AcceptTCP()
		recipientConn.SetKeepAlive(true)
		if err != nil {
			utils.StacktraceErrorAndExit(err)
			return
		}
		log.Println("New recipient connection from", Conn(recipientConn))
		donorConn := <-donorConns
		proxy.ProxifyConns(recipientConn, donorConn)
	}
}

机器B的隧道(Node.js):

import net, { AddressInfo } from 'net';
import http from 'http';
import golgi from 'golgi';

export const startHttpProxy = () => {
  const server = http.createServer();
  let proxyServer: http.Server = golgi(server);
  // Listening to 0 assigns a random OS-assigned port
  proxyServer = proxyServer.listen(0);
  return proxyServer;
};

export const startDonorSocket = () => {
  const proxyServer = startHttpProxy();
  const proxyServerSocket = new net.Socket();
  proxyServerSocket.connect(
    (proxyServer.address() as AddressInfo).port,
    '127.0.0.1'
  );

  const donorSocket = new net.Socket();
  donorSocket.setKeepAlive(true);
  donorSocket.connect(11000, '2.226.102.14', () => {
    proxyServerSocket.pipe(donorSocket);
    donorSocket.pipe(proxyServerSocket);
  });
};

不幸的是,这在隧道到一个TCP地址时有效,但在隧道到多个TCP地址时无效。如果我打开多个机器B的隧道(Node.js代码),它就有效。我的意思是,捐赠者连接(Node.js)每次被接收者(HTTP代理用户)使用时都会被“消耗”,因为在其上建立了一个持久的TCP隧道。

我想知道是否有一种方法可以使其适用于任意数量的TCP连接,而不仅仅是一个。我现在唯一的想法是每次消耗一个连接时创建更多的TCP捐赠者连接,但我想知道是否有更简单的解决方案。

英文:

I'm trying to create a reverse proxy to a CONNECT-based HTTP proxy. The user who wants to use the proxy just treats machine A as an HTTP proxy. It works the following way:

  1. machine B opens a TCP socket to machine A.
  2. On machine A, a TCP socket is exposed on a port and all the incoming data is tunneled to machine B (io.Copy).
  3. On machine B, all the data is tunneled to the local HTTP server and the socket to machine A.

Essentially this is a reverse-proxy behind an HTTP proxy. The reason it's this complex is because the HTTP proxy is behind NAT (on machine B) and therefore not accessible directly. The use case is being able to host an HTTP proxy behind a NAT.

Machine A tunnel (Go):

package main
import (
&quot;log&quot;
&quot;net&quot;
)
func Conn(c *net.TCPConn) string {
return c.RemoteAddr().String() + &quot; (&quot; + c.LocalAddr().String() + &quot;)&quot;
}
func ProxifyConns(recipientConn, donorConn *net.TCPConn) {
log.Println(&quot;Proxying&quot;, ConnrecipientConn), &quot;and&quot;, Conn(donorConn))
go func() {
_, err := io.Copy(recipientConn, donorConn)
if err != nil {
utils.StacktraceError(err)
}
recipientConn.Close()
}()
go func() {
_, err := io.Copy(donorConn, recipientConn)
if err != nil {
utils.StacktraceError(err)
}
recipientConn.Close()
}()
}
func main() {
// Open the donor listener
donorsAddr, err := net.ResolveTCPAddr(&quot;tcp4&quot;, &quot;:11000&quot;)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
listenerDonors, err := net.ListenTCP(&quot;tcp&quot;, donorsAddr)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
defer listenerDonors.Close()
log.Println(&quot;Listening for donors on&quot;, listenerDonors.Addr())
// Open the recipient listener
recipientsAddr, err := net.ResolveTCPAddr(&quot;tcp4&quot;, &quot;:10000&quot;)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
listenerRecipients, err := net.ListenTCP(&quot;tcp&quot;, recipientsAddr)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
defer listenerRecipients.Close()
log.Println(&quot;Listening for recipients on&quot;, listenerRecipients.Addr())
// 	Handle donor connections
donorConns := make(chan *net.TCPConn)
go func() {
for {
donorConn, err := listenerDonors.AcceptTCP()
donorConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println(&quot;New donor connection from&quot;, Conn(donorConn))
donorConns &lt;- donorConn
}
}()
// Handle recipient connections
for {
recipientConn, err := listenerRecipients.AcceptTCP()
recipientConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println(&quot;New recipient connection from&quot;, Conn(recipientConn))
donorConn := &lt;-donorConns
proxy.ProxifyConns(recipientConn, donorConn)
}
}

Machine B tunnel (Node.js):

import net, { AddressInfo } from &#39;net&#39;;
import http from &#39;http&#39;;
import golgi from &#39;golgi&#39;;
export const startHttpProxy = () =&gt; {
const server = http.createServer();
let proxyServer: http.Server = golgi(server);
// Listening to 0 assigns a random OS-assigned port
proxyServer = proxyServer.listen(0);
return proxyServer;
};
export const startDonorSocket = () =&gt; {
const proxyServer = startHttpProxy();
const proxyServerSocket = new net.Socket();
proxyServerSocket.connect(
(proxyServer.address() as AddressInfo).port,
&#39;127.0.0.1&#39;
);
const donorSocket = new net.Socket();
donorSocket.setKeepAlive(true);
donorSocket.connect(11000, &#39;2.226.102.14&#39;, () =&gt; {
proxyServerSocket.pipe(donorSocket);
donorSocket.pipe(proxyServerSocket);
});
};

Unfortunately this works when tunneling to one TCP address but not when tunneling to more. If I open many Machine B tunnels (Node.js code), it works. What I mean is that a donor connection (Node.js) is "consumed" ever time it is taken by a recipient (HTTP proxy user) because a persistent TCP tunnel is made on it.

I wonder is there is a way to make this work for any amount of TCP connections, not just one. My only idea right now is to create more TCP donor connections every time a connection is consumed but I wonder if there is a simpler solution.

答案1

得分: 1

当你执行以下代码时:

go func() {
    for {
        donorConn, err := listenerDonors.AcceptTCP()
        donorConn.SetKeepAlive(true)
        if err != nil {
            utils.StacktraceErrorAndExit(err)
            return
        }
        log.Println("New donor connection from", Conn(donorConn))
        donorConns <- donorConn
    }
}()

你开始处理第一个TCP连接。这段代码在donorConns <- donorConn这一行阻塞。在这个发送到通道的操作完成之前,循环不会进入第二次迭代(也就是下一个TCP连接不会被接受)。

你进行了一个非常类似的第二个循环:

// 处理接收者连接
for {
    recipientConn, err := listenerRecipients.AcceptTCP()
    recipientConn.SetKeepAlive(true)
    if err != nil {
        utils.StacktraceErrorAndExit(err)
        return
    }
    log.Println("New recipient connection from", Conn(recipientConn))
    donorConn := <-donorConns
    proxy.ProxifyConns(recipientConn, donorConn)
}

这个循环需要等待donorConn := <-donorConns(来自第一个循环)完成,并且需要等待proxy.ProxifyConns(recipientConn, donorConn)完成。

我不确定你希望整个代码如何工作,但很可能你只需要进行一个非常小的更改:
go proxy.ProxifyConns(recipientConn, donorConn)

英文:

When you do

go func() {
for {
donorConn, err := listenerDonors.AcceptTCP()
donorConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println(&quot;New donor connection from&quot;, Conn(donorConn))
donorConns &lt;- donorConn
}
}()

You start processing the first TCP connection. This code blocks on donorConns &lt;- donorConn. Until this send to channel finishes the loop won't go into the second iteration (and the next TCP connection won't be accepted).

You do a very similar second loop

// Handle recipient connections
for {
recipientConn, err := listenerRecipients.AcceptTCP()
recipientConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println(&quot;New recipient connection from&quot;, Conn(recipientConn))
donorConn := &lt;-donorConns
proxy.ProxifyConns(recipientConn, donorConn)
}

which requires donorConn := &lt;-donorConns to complete (from the first loop) and requires proxy.ProxifyConns(recipientConn, donorConn) to complete.

I'm not sure how you intend the whole thing to work, but, most likely, you need a very minor change:
go proxy.ProxifyConns(recipientConn, donorConn)

huangapple
  • 本文由 发表于 2022年1月3日 00:37:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/70557765.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定