commit 666db25b3f04613d21eefb0cad2855b1520bc808 Author: GuanM <30427262+sxhoio@users.noreply.github.com.> Date: Sat Oct 5 16:59:18 2024 +0800 初始化提交 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e0419d4 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module zhuabao + +go 1.22 + +require github.com/miekg/dns v1.1.62 + +require ( + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/tools v0.22.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..95e8194 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= diff --git a/main.go b/main.go new file mode 100644 index 0000000..71a2ab7 --- /dev/null +++ b/main.go @@ -0,0 +1,404 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "math/big" + "net" + "net/http" + "net/url" + "os" + "path/filepath" + "time" + "github.com/miekg/dns" +) + +// 添加全局变量 +var ( + proxyAddress string // 默认值,可以通过命令行参数修改 + listenAddress string // 新增的地址代理参数 + dnsPort string // 新增DNS服务器端口 +) + +func main() { + // 解析命令行参数 + flag.StringVar(&listenAddress, "address", "127.0.0.1", "HTTP和HTTPS监听地址") + flag.StringVar(&proxyAddress, "address-proxy", "127.0.0.1:7897", "代理服务器地址") + flag.StringVar(&dnsPort, "dns-port", "53", "DNS服务器端口") + flag.Parse() + + // 检查CA证书是否存在 + sslDir := "SSL" + caFile := filepath.Join(sslDir, "ca.pem") + keyFile := filepath.Join(sslDir, "ca-key.pem") + if !fileExists(caFile) || !fileExists(keyFile) { + generateCACert(caFile, keyFile) + } + + // 加载CA证书和私钥 + caCert, caKey, err := loadCA(caFile, keyFile) + if err != nil { + log.Fatal("加载CA证书和私钥失败:", err) + } + + // 设置HTTP服务器 + go func() { + httpServer := &http.Server{ + Addr: listenAddress + ":80", + Handler: http.HandlerFunc(httpHandler), + } + log.Printf("启动HTTP服务器,监听地址: %s:80\n", listenAddress) + if err := httpServer.ListenAndServe(); err != nil { + log.Fatal("HTTP服务器错误:", err) + } + }() + + // 设置HTTPS服务器 + httpsServer := &http.Server{ + Addr: listenAddress + ":443", + TLSConfig: &tls.Config{ + GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + sni := hello.ServerName + fmt.Printf("接收到HTTPS请求,SNI: %s\n", sni) + return generateCert(caCert, caKey, sni) + }, + }, + Handler: http.HandlerFunc(httpsHandler), + } + + log.Printf("启动HTTPS服务器,监听地址: %s:443, 代理地址: %s...\n", listenAddress, proxyAddress) + if err := httpsServer.ListenAndServeTLS("", ""); err != nil { + log.Fatal("HTTPS服务器错误:", err) + } + + // 启动DNS服务器 + go startDNSServer() +} + +func httpHandler(w http.ResponseWriter, r *http.Request) { + // 实现HTTP代理逻辑 + if r.Method == http.MethodConnect { + handleHTTPS(w, r) + } else { + handleHTTP(w, r) + } +} + +func handleHTTP(w http.ResponseWriter, r *http.Request) { + // 用全局变量创建代理URL + proxyURL, err := url.Parse("http://" + proxyAddress) + if err != nil { + http.Error(w, "解析代理URL失败", http.StatusInternalServerError) + return + } + + // 创建一个使用HTTP代理的客户端 + proxyClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + }, + } + + // 创建一个新的请求 + proxyReq, err := http.NewRequest(r.Method, r.URL.String(), r.Body) + if err != nil { + http.Error(w, "创建代理请求失败", http.StatusInternalServerError) + return + } + + // 复制原始请求的头部 + for name, values := range r.Header { + for _, value := range values { + proxyReq.Header.Add(name, value) + } + } + + // 发送代理请求 + resp, err := proxyClient.Do(proxyReq) + if err != nil { + http.Error(w, "代理请求失败", http.StatusBadGateway) + return + } + defer resp.Body.Close() + + // 复制响应头部 + for name, values := range resp.Header { + for _, value := range values { + w.Header().Add(name, value) + } + } + + // 设置状态码 + w.WriteHeader(resp.StatusCode) + + // 复制响应体 + io.Copy(w, resp.Body) + + // 设置Connection头为close,确保客户端知道连接将被关闭 + w.Header().Set("Connection", "close") +} + +func handleHTTPS(w http.ResponseWriter, r *http.Request) { + // 建立到目标服务器的连接 + destConn, err := net.Dial("tcp", r.Host) + if err != nil { + http.Error(w, err.Error(), http.StatusServiceUnavailable) + return + } + defer destConn.Close() + + // 向客户端发送200 OK响应 + w.WriteHeader(http.StatusOK) + + // 获取底层的TCP连接 + hijacker, ok := w.(http.Hijacker) + if !ok { + http.Error(w, "Hijacking not supported", http.StatusInternalServerError) + return + } + clientConn, _, err := hijacker.Hijack() + if err != nil { + http.Error(w, err.Error(), http.StatusServiceUnavailable) + return + } + defer clientConn.Close() + + // 在客户端和目标服务器之间转发数据 + go io.Copy(destConn, clientConn) + io.Copy(clientConn, destConn) +} + +func fileExists(filename string) bool { + _, err := os.Stat(filename) + return !os.IsNotExist(err) +} + +func generateCACert(caFile, keyFile string) { + // 生成CA私钥 + caKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatal("生成CA私钥失败:", err) + } + + // 创建CA证书模板 + caTemplate := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "自签名CA", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + + // 创建CA证书 + caCertDER, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey) + if err != nil { + log.Fatal("创建CA证书失败:", err) + } + + // 确保SSL目录存在 + sslDir := filepath.Dir(caFile) + if err := os.MkdirAll(sslDir, 0755); err != nil { + log.Fatal("创建SSL目录失败:", err) + } + + // 将CA证书写入文件 + caCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caCertDER}) + if err := ioutil.WriteFile(caFile, caCertPEM, 0644); err != nil { + log.Fatal("写入CA证书失败:", err) + } + + // 将CA私钥写入文件 + caKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(caKey)}) + if err := ioutil.WriteFile(keyFile, caKeyPEM, 0600); err != nil { + log.Fatal("写入CA私钥失败:", err) + } + + log.Println("生成了新的CA证书") +} + +func loadCA(caFile, keyFile string) (*x509.Certificate, *rsa.PrivateKey, error) { + // 读取CA证书 + caCertPEM, err := ioutil.ReadFile(caFile) + if err != nil { + return nil, nil, fmt.Errorf("读取CA证书失败: %v", err) + } + caCertBlock, _ := pem.Decode(caCertPEM) + if caCertBlock == nil { + return nil, nil, fmt.Errorf("解码CA证书失败") + } + caCert, err := x509.ParseCertificate(caCertBlock.Bytes) + if err != nil { + return nil, nil, fmt.Errorf("解析CA证书失败: %v", err) + } + + // 读取CA私钥 + caKeyPEM, err := ioutil.ReadFile(keyFile) + if err != nil { + return nil, nil, fmt.Errorf("读取CA私钥失败: %v", err) + } + caKeyBlock, _ := pem.Decode(caKeyPEM) + if caKeyBlock == nil { + return nil, nil, fmt.Errorf("解码CA私钥失败") + } + caKey, err := x509.ParsePKCS1PrivateKey(caKeyBlock.Bytes) + if err != nil { + return nil, nil, fmt.Errorf("解析CA私钥失败: %v", err) + } + + return caCert, caKey, nil +} + +func generateCert(caCert *x509.Certificate, caKey *rsa.PrivateKey, serverName string) (*tls.Certificate, error) { + // 生成服务器私钥 + serverKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("生成服务器私钥失败: %v", err) + } + + // 创建服务器证书模板 + serverTemplate := x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{ + CommonName: serverName, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + DNSNames: []string{serverName}, + } + + // 使用CA签发服务器证书 + serverCertDER, err := x509.CreateCertificate(rand.Reader, &serverTemplate, caCert, &serverKey.PublicKey, caKey) + if err != nil { + return nil, fmt.Errorf("创建服务器证书失败: %v", err) + } + + // 将证书和私钥转换为PEM格式 + serverCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCertDER}) + serverKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(serverKey)}) + + // 创建SSL文件夹和SNI子文件夹 + sslDir := "SSL" + sniDir := filepath.Join(sslDir, serverName) + if err := os.MkdirAll(sniDir, 0755); err != nil { + return nil, fmt.Errorf("创建目录失败: %v", err) + } + + // 保存证书和私钥到SNI子文件夹 + certFile := filepath.Join(sniDir, serverName+".pem") + keyFile := filepath.Join(sniDir, serverName+"-key.pem") + if err := ioutil.WriteFile(certFile, serverCertPEM, 0644); err != nil { + return nil, fmt.Errorf("写入服务器证书失败: %v", err) + } + if err := ioutil.WriteFile(keyFile, serverKeyPEM, 0600); err != nil { + return nil, fmt.Errorf("写入服务器私钥失败: %v", err) + } + + // 创tls.Certificate对象 + cert, err := tls.X509KeyPair(serverCertPEM, serverKeyPEM) + if err != nil { + return nil, fmt.Errorf("创建X509密钥对失败: %v", err) + } + + return &cert, nil +} + +func httpsHandler(w http.ResponseWriter, r *http.Request) { + // 确保URL使用HTTPS协议并包含正确的Host + r.URL.Scheme = "https" + if r.URL.Host == "" { + r.URL.Host = r.Host + } + + // 创建正向代理客户端 + proxyClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(&url.URL{ + Scheme: "http", + Host: proxyAddress, + }), + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DisableKeepAlives: true, // 禁用Keep-Alive + }, + } + + // 创建一个新的请求 + proxyReq, err := http.NewRequest(r.Method, r.URL.String(), r.Body) + if err != nil { + http.Error(w, "创建代理请求失败", http.StatusInternalServerError) + return + } + // 复制原始请求的头部 + proxyReq.Header = r.Header.Clone() + + // 确保请求体被关闭 + if r.Body != nil { + defer r.Body.Close() + } + + // 发送代理请求 + resp, err := proxyClient.Do(proxyReq) + if err != nil { + http.Error(w, "代理请求失败: "+err.Error(), http.StatusBadGateway) + return + } + defer resp.Body.Close() + + // 复制响应头部和状态码 + for name, values := range resp.Header { + w.Header()[name] = values + } + w.WriteHeader(resp.StatusCode) + + // 复制响应体 + _, err = io.Copy(w, resp.Body) + if err != nil { + log.Printf("复制响应体时发生错误: %v", err) + } + + // 设置Connection头为close,确保客户端知道连接将被关闭 + w.Header().Set("Connection", "close") +} + +func startDNSServer() { + dns.HandleFunc(".", handleDNSRequest) + server := &dns.Server{Addr: ":" + dnsPort, Net: "udp"} + log.Printf("启动DNS服务器,监听端口: %s\n", dnsPort) + err := server.ListenAndServe() + if err != nil { + log.Fatalf("启动DNS服务器失败: %v", err) + } +} + +func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + + for _, q := range m.Question { + switch q.Qtype { + case dns.TypeA: + rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, listenAddress)) + if err == nil { + m.Answer = append(m.Answer, rr) + } + } + } + + w.WriteMsg(m) +}