mic~ proxy for JA4-TLS fingerprinting evasion |
| git clone https://github.com/djnnvx/mic.git |
| log | files | refs | archive | README | LICENSE |
https.go (2456B)
1 package proxy 2 3 import ( 4 "bufio" 5 "crypto/tls" 6 "log" 7 "net" 8 "net/http" 9 "time" 10 ) 11 12 // Bounds the CONNECT header read only, never the proxied stream. 13 var headerReadTimeout = 10 * time.Second 14 15 // parseAuthority splits a CONNECT authority into a server name and a dial-ready 16 // "host:port", assuming port 443 and stripping IPv6 brackets when needed. 17 func parseAuthority(authority string) (server, hostPort string) { 18 if h, _, err := net.SplitHostPort(authority); err == nil { 19 return h, authority 20 } 21 server = authority 22 if n := len(server); n >= 2 && server[0] == '[' && server[n-1] == ']' { 23 server = server[1 : n-1] 24 } 25 return server, net.JoinHostPort(server, "443") 26 } 27 28 func HttpsHandler(clientConn net.Conn, p *Proxy) { 29 defer clientConn.Close() 30 31 reader := bufio.NewReader(clientConn) 32 clientConn.SetReadDeadline(time.Now().Add(headerReadTimeout)) 33 req, err := http.ReadRequest(reader) 34 clientConn.SetReadDeadline(time.Time{}) 35 if err != nil { 36 log.Printf("Failed to read client request: %v", err) 37 return 38 } 39 40 if req.Method != http.MethodConnect { 41 log.Printf("Received non-CONNECT request: %s %s", req.Method, req.Host) 42 return 43 } 44 45 serverName, host := parseAuthority(req.URL.Host) 46 47 log.Printf("Connecting to target: %s", host) 48 targetConn, err := p.dialTarget(host) 49 if err != nil { 50 log.Printf("Failed to connect to target %s: %v", host, err) 51 return 52 } 53 defer targetConn.Close() 54 55 clientConn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) 56 57 if p.LocalCA != nil { 58 cert, err := p.LocalCA.issueCert(serverName) 59 if err != nil { 60 log.Printf("Failed to issue cert for %s: %v", serverName, err) 61 return 62 } 63 64 // Mirror the ALPN negotiated with the target: otherwise uTLS often 65 // negotiates h2 upstream while the client speaks HTTP/1.1, which fails. 66 nextProtos := []string{"http/1.1"} 67 if proto := targetConn.ConnectionState().NegotiatedProtocol; proto != "" { 68 nextProtos = []string{proto} 69 } 70 71 tlsClient := tls.Server(clientConn, &tls.Config{ 72 Certificates: []tls.Certificate{cert}, 73 NextProtos: nextProtos, 74 }) 75 if err := tlsClient.Handshake(); err != nil { 76 log.Printf("TLS handshake with client failed for %s: %v", serverName, err) 77 return 78 } 79 defer tlsClient.Close() 80 81 log.Printf("MitM tunnel established for %s (proto: %s)", serverName, tlsClient.ConnectionState().NegotiatedProtocol) 82 pipe(tlsClient, targetConn) 83 return 84 } 85 86 pipe(&bufferedConn{r: reader, Conn: clientConn}, targetConn) 87 }