mic.git


mic

~ proxy for JA4-TLS fingerprinting evasion
git clone https://github.com/djnnvx/mic.git
log | files | refs | archive | README | LICENSE

commit 5f5119730f5de89c3ee83a823243092d9ed14a6f
parent 9695b1e40189a62a01dcb87d0bdccc2da6915955
Author: djnn <[email protected]>
Date: Sun, 17 May 2026 18:50:17 +0900

chore(net): minor refactorings

Diffstat:
Mcmd/probe/main.go | 23+++++++++--------------
Mproxy/https.go | 4++--
Mproxy/proxy.go | 25++++++++++++++++++-------
Mproxy/serverfront.go | 3+--

4 files changed, 30 insertions(+), 25 deletions(-)

diff --git a/cmd/probe/main.go b/cmd/probe/main.go
@@ -5,7 +5,6 @@ package main
 import (
 	"bufio"
 	"context"
-	"crypto/tls"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -74,19 +73,16 @@ func probe(id utls.ClientHelloID) (string, error) {
 	}
 	defer uconn.Close()
 
-	proto := uconn.ConnectionState().NegotiatedProtocol
-
-	if proto == "h2" {
-		// Use http2.Transport with the already-dialed connection.
-		h2t := &http2.Transport{
-			DialTLSContext: func(ctx context.Context, network, addr string, _ *tls.Config) (net.Conn, error) {
-				return dialUTLS(ctx, id)
-			},
+	if uconn.ConnectionState().NegotiatedProtocol == "h2" {
+		// Reuse the already-established uconn rather than letting the Transport
+		// dial again — the first handshake is the one that produced the JA4 we
+		// want to measure.
+		cc, err := (&http2.Transport{}).NewClientConn(uconn)
+		if err != nil {
+			return "", err
 		}
-		// Discard the first connection — the Transport dials its own.
-		uconn.Close()
-		client := &http.Client{Transport: h2t}
-		resp, err := client.Get("https://tlsinfo.me/json")
+		req, _ := http.NewRequest("GET", "https://tlsinfo.me/json", nil)
+		resp, err := cc.RoundTrip(req)
 		if err != nil {
 			return "", err
 		}
@@ -94,7 +90,6 @@ func probe(id utls.ClientHelloID) (string, error) {
 		return decodeJA4(resp.Body)
 	}
 
-	// http/1.1 path: reuse the already-established connection.
 	req, _ := http.NewRequest("GET", "/json", nil)
 	req.Host = "tlsinfo.me"
 	req.Header.Set("Connection", "close")
diff --git a/proxy/https.go b/proxy/https.go
@@ -77,9 +77,9 @@ func HttpsHandler(clientConn net.Conn, p *Proxy) {
 		defer tlsClient.Close()
 
 		log.Printf("MitM tunnel established for %s (proto: %s)", serverName, tlsClient.ConnectionState().NegotiatedProtocol)
-		pipe(tlsClient, tlsClient, targetConn)
+		pipe(tlsClient, targetConn)
 		return
 	}
 
-	pipe(clientConn, reader, targetConn)
+	pipe(&bufferedConn{r: reader, Conn: clientConn}, targetConn)
 }
diff --git a/proxy/proxy.go b/proxy/proxy.go
@@ -1,6 +1,7 @@
 package proxy
 
 import (
+	"bufio"
 	"crypto/x509"
 	"fmt"
 	"io"
@@ -55,22 +56,32 @@ func (p *Proxy) dialTarget(host string) (*utls.UConn, error) {
 	return uconn, nil
 }
 
-// pipe copies bidirectionally between client (reading from r) and target.
-// When either direction finishes it closes that side so the other goroutine unblocks.
-func pipe(client io.ReadWriteCloser, r io.Reader, target io.ReadWriteCloser) {
+// bufferedConn pairs a net.Conn with a bufio.Reader that may already contain
+// data read ahead from the underlying conn (e.g. after http.ReadRequest).
+// Reads come from the buffer; writes and Close go to the conn.
+type bufferedConn struct {
+	r *bufio.Reader
+	net.Conn
+}
+
+func (b *bufferedConn) Read(p []byte) (int, error) { return b.r.Read(p) }
+
+// pipe copies bidirectionally between a and b. When either direction finishes
+// it closes that side so the other goroutine unblocks.
+func pipe(a, b io.ReadWriteCloser) {
 	var wg sync.WaitGroup
 	wg.Add(2)
 
 	go func() {
 		defer wg.Done()
-		io.Copy(target, r)
-		target.Close()
+		io.Copy(b, a)
+		b.Close()
 	}()
 
 	go func() {
 		defer wg.Done()
-		io.Copy(client, target)
-		client.Close()
+		io.Copy(a, b)
+		a.Close()
 	}()
 
 	wg.Wait()
diff --git a/proxy/serverfront.go b/proxy/serverfront.go
@@ -1,7 +1,6 @@
 package proxy
 
 import (
-	"bufio"
 	"crypto/tls"
 	"fmt"
 	"log"
@@ -38,6 +37,6 @@ func ServerFrontHandler(certFile, keyFile string) (Handler, error) {
 		}
 		defer targetConn.Close()
 
-		pipe(tlsConn, bufio.NewReader(tlsConn), targetConn)
+		pipe(tlsConn, targetConn)
 	}, nil
 }