#起因
因为 OpenAI 网站偶尔要重新登录,使用并不方便。前几天搭了一个 ChatGPT 的站,帮助自己快速使用 GPT,chat.acytoo.com,没想到就导致域名被墙了,细节请见这篇日记
#思路
虽然是面向世界建站,但是也有兴趣研究一下其中的过程。发现了一篇博客,介绍了 TCP 阻断的原理等,讲的很好:301海外跳转原理解析兼谈缓解假墙伪墙攻击勒索的多种技术手段(1),看了文章想自己试一下 HTTP 抢答式 302 跳转,并不能解决网站被墙的问题,可能有助于缓解历史上被墙初期的 TCP 阻断问题,并不适用与现在的防火墙。可能有助于搭建中转站。 因为域名已被 DNS 污染,救不回来了,只是学习技术。
因原文用 用Python编写的(甚至都没有使用asyncio),性能会比较差一些,这里用 golang 重写,并且用 caddy 反代,实战一下。
思路:在正常访问网站时,3 次握手后,由客户端请求,服务器完成客户的请求。被墙初期,连接到网站是可以完成 3 次握手的,然后客户端发送请求,被检测,被阻断。现在我们在 3 次握手后,服务器主动,直接发送 302/301 跳转给客户,客户没有主动过墙请求任何数据,理论上这个网站不会被干扰,以后如果墙了跳转到的域名,直接换跳转域名,而用户总有一个“网址发布站”可以访问。历史上,很多成人站,菠菜站,采用这种思路。
#代码
本代码由 ChatGPT 完成,ChatGPT 还提出了几种继续优化的方法,包括使用 sync.Pool
等。
package main
import (
"log"
"net"
)
var response = []byte("HTTP/1.1 302 Moved Temporarily\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 0\r\n" +
"Connection: close\r\n" +
"Location: https://chat.acytoo.net/\r\n\r\n")
func main() {
listener, err := net.Listen("tcp", "0.0.0.0:30081")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
conn.(*net.TCPConn).SetLinger(0)
conn.(*net.TCPConn).SetNoDelay(true)
_, err := conn.Write(response)
if err != nil {
log.Println("Error writing response:", err)
}
}
为“尊重 ChatGPT 版权”😂,我并没有修改任何代码。实际上,golang net 默认不使用 Nagle 算法,因此不需要 SetLinger(0)
与 SetNoDelay(true)
这两个控制语句。
上面的 golang 程序开了 30081 端口,接收 TCP 连接,在握手后,直接发送 302 redirection
可以用 Caddyfile 反代一下。
chat.acytoo.com {
reverse_proxy localhost:30081
}
#效果
使用 wireshark 抓包,看连接过程。下图左为服务器发送给客户的 302 跳转包,右为客户端对服务器发的请求。
三次握手后,服务端主动发送了 302 包,seq 为 2793964333,ack 为 3053880358,客户端发送 ack 为 2793964333 的请求包,表示已经收到了 302 包。客户端这时发送的 seq 才为 3053879832。可惜客户端在收到了 302 跳转后,继续发送了请求,这在原博主的文章中,有介绍与解决方法,但是因为 HTTP 跳转实在没什么用,不继续搞了。