初始化提交

This commit is contained in:
GuanM 2024-10-05 16:59:18 +08:00
commit 666db25b3f
3 changed files with 429 additions and 0 deletions

13
go.mod Normal file
View File

@ -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
)

12
go.sum Normal file
View File

@ -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=

404
main.go Normal file
View File

@ -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)
}