mic.git


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 }