HTTPSniffer/main.go
2024-10-05 16:59:18 +08:00

405 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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