英文:
Make reverse TCP connection accept any amount of connections (like a normal TCP server)
问题
我正在尝试创建一个反向代理到基于CONNECT的HTTP代理。想要使用代理的用户将机器A
视为HTTP代理。它的工作方式如下:
机器B
打开一个TCP套接字到机器A
。- 在
机器A
上,一个TCP套接字在一个端口上暴露,并且所有传入的数据都被隧道传输到机器B
(io.Copy)。 - 在
机器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:
machine B
opens a TCP socket tomachine A
.- On
machine A
, a TCP socket is exposed on a port and all the incoming data is tunneled tomachine B
(io.Copy). - On
machine B
, all the data is tunneled to the local HTTP server and the socket tomachine 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 (
"log"
"net"
)
func Conn(c *net.TCPConn) string {
return c.RemoteAddr().String() + " (" + c.LocalAddr().String() + ")"
}
func ProxifyConns(recipientConn, donorConn *net.TCPConn) {
log.Println("Proxying", ConnrecipientConn), "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() {
// Open the donor listener
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())
// Open the recipient listener
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())
// 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("New donor connection from", Conn(donorConn))
donorConns <- donorConn
}
}()
// Handle recipient connections
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)
}
}
Machine B tunnel (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);
});
};
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("New donor connection from", Conn(donorConn))
donorConns <- donorConn
}
}()
You start processing the first TCP connection. This code blocks on donorConns <- 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("New recipient connection from", Conn(recipientConn))
donorConn := <-donorConns
proxy.ProxifyConns(recipientConn, donorConn)
}
which requires donorConn := <-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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论