mic.git


mic

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

proxy_test.go (4876B)

      1 package proxy
      2 
      3 import (
      4 	"crypto/ecdsa"
      5 	"crypto/elliptic"
      6 	"crypto/rand"
      7 	"crypto/tls"
      8 	"crypto/x509"
      9 	"crypto/x509/pkix"
     10 	"errors"
     11 	"io"
     12 	"math/big"
     13 	"net"
     14 	"strings"
     15 	"testing"
     16 	"time"
     17 
     18 	utls "github.com/bogdanfinn/utls"
     19 	"github.com/djnnvx/mic/fingerprint"
     20 )
     21 
     22 func generateSelfSignedCert(t *testing.T) (*x509.CertPool, tls.Certificate) {
     23 	t.Helper()
     24 
     25 	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
     26 	if err != nil {
     27 		t.Fatalf("generating key: %v", err)
     28 	}
     29 
     30 	tmpl := &x509.Certificate{
     31 		SerialNumber: big.NewInt(1),
     32 		Subject:      pkix.Name{CommonName: "localhost"},
     33 		NotBefore:    time.Now().Add(-time.Hour),
     34 		NotAfter:     time.Now().Add(time.Hour),
     35 		DNSNames:     []string{"localhost"},
     36 		IPAddresses:  []net.IP{net.ParseIP("127.0.0.1")},
     37 	}
     38 
     39 	certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
     40 	if err != nil {
     41 		t.Fatalf("creating certificate: %v", err)
     42 	}
     43 
     44 	cert, err := x509.ParseCertificate(certDER)
     45 	if err != nil {
     46 		t.Fatalf("parsing certificate: %v", err)
     47 	}
     48 
     49 	pool := x509.NewCertPool()
     50 	pool.AddCert(cert)
     51 
     52 	tlsCert := tls.Certificate{
     53 		Certificate: [][]byte{certDER},
     54 		PrivateKey:  key,
     55 	}
     56 
     57 	return pool, tlsCert
     58 }
     59 
     60 func TestDialTarget_HandshakeSucceeds(t *testing.T) {
     61 	pool, tlsCert := generateSelfSignedCert(t)
     62 
     63 	ln, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
     64 		Certificates: []tls.Certificate{tlsCert},
     65 	})
     66 	if err != nil {
     67 		t.Fatalf("tls.Listen: %v", err)
     68 	}
     69 	defer ln.Close()
     70 
     71 	go func() {
     72 		conn, err := ln.Accept()
     73 		if err != nil {
     74 			return
     75 		}
     76 		defer conn.Close()
     77 		io.Copy(io.Discard, conn)
     78 	}()
     79 
     80 	fp, err := fingerprint.ByName("chrome-120")
     81 	if err != nil {
     82 		t.Fatalf("fingerprint.ByName: %v", err)
     83 	}
     84 
     85 	p := &Proxy{
     86 		ListenAddr:  "127.0.0.1:0",
     87 		CAPool:      pool,
     88 		Fingerprint: fp,
     89 	}
     90 
     91 	conn, err := p.dialTarget(ln.Addr().String())
     92 	if err != nil {
     93 		t.Fatalf("dialTarget(%q): %v", ln.Addr().String(), err)
     94 	}
     95 	defer conn.Close()
     96 
     97 	if _, err := conn.Write([]byte("hello\n")); err != nil {
     98 		t.Fatalf("writing to connection: %v", err)
     99 	}
    100 }
    101 
    102 func TestDialTarget_FallbackToRandomized(t *testing.T) {
    103 	pool, tlsCert := generateSelfSignedCert(t)
    104 
    105 	ln, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
    106 		Certificates: []tls.Certificate{tlsCert},
    107 	})
    108 	if err != nil {
    109 		t.Fatalf("tls.Listen: %v", err)
    110 	}
    111 	defer ln.Close()
    112 
    113 	go func() {
    114 		conn, err := ln.Accept()
    115 		if err != nil {
    116 			return
    117 		}
    118 		defer conn.Close()
    119 		io.Copy(io.Discard, conn)
    120 	}()
    121 
    122 	p := &Proxy{
    123 		ListenAddr: "127.0.0.1:0",
    124 		CAPool:     pool,
    125 	}
    126 
    127 	conn, err := p.dialTarget(ln.Addr().String())
    128 	if err != nil {
    129 		// HelloRandomized sometimes picks a curve the local TLS stack rejects.
    130 		if !strings.Contains(err.Error(), "CurvePreferences includes unsupported curve") {
    131 			t.Fatalf("dialTarget with randomized preset: %v", err)
    132 		}
    133 		return
    134 	}
    135 	defer conn.Close()
    136 
    137 	if got := conn.ClientHelloID.Str(); got != utls.HelloRandomized.Str() {
    138 		t.Fatalf("ClientHelloID: got %q, want %q", got, utls.HelloRandomized.Str())
    139 	}
    140 }
    141 
    142 func TestServe_ReturnsWhenListenerClosed(t *testing.T) {
    143 	ln, err := net.Listen("tcp", "127.0.0.1:0")
    144 	if err != nil {
    145 		t.Fatalf("net.Listen: %v", err)
    146 	}
    147 
    148 	p := &Proxy{Handler: func(conn net.Conn, _ *Proxy) { conn.Close() }}
    149 
    150 	done := make(chan error, 1)
    151 	go func() { done <- p.Serve(ln) }()
    152 
    153 	time.Sleep(50 * time.Millisecond)
    154 	ln.Close()
    155 
    156 	select {
    157 	case err := <-done:
    158 		if !errors.Is(err, net.ErrClosed) {
    159 			t.Fatalf("Serve returned %v, want net.ErrClosed", err)
    160 		}
    161 	case <-time.After(3 * time.Second):
    162 		t.Fatal("Serve did not return after the listener was closed")
    163 	}
    164 }
    165 
    166 func TestPipe_HalfCloseDeliversResponse(t *testing.T) {
    167 	backend, err := net.Listen("tcp", "127.0.0.1:0")
    168 	if err != nil {
    169 		t.Fatalf("net.Listen backend: %v", err)
    170 	}
    171 	defer backend.Close()
    172 
    173 	go func() {
    174 		conn, err := backend.Accept()
    175 		if err != nil {
    176 			return
    177 		}
    178 		defer conn.Close()
    179 		io.Copy(io.Discard, conn)
    180 		time.Sleep(100 * time.Millisecond)
    181 		conn.Write([]byte("PONG"))
    182 	}()
    183 
    184 	front, err := net.Listen("tcp", "127.0.0.1:0")
    185 	if err != nil {
    186 		t.Fatalf("net.Listen front: %v", err)
    187 	}
    188 	defer front.Close()
    189 
    190 	go func() {
    191 		conn, err := front.Accept()
    192 		if err != nil {
    193 			return
    194 		}
    195 		upstream, err := net.Dial("tcp", backend.Addr().String())
    196 		if err != nil {
    197 			conn.Close()
    198 			return
    199 		}
    200 		pipe(conn, upstream)
    201 	}()
    202 
    203 	client, err := net.Dial("tcp", front.Addr().String())
    204 	if err != nil {
    205 		t.Fatalf("net.Dial front: %v", err)
    206 	}
    207 	defer client.Close()
    208 
    209 	if _, err := client.Write([]byte("PING")); err != nil {
    210 		t.Fatalf("writing request: %v", err)
    211 	}
    212 	if err := client.(*net.TCPConn).CloseWrite(); err != nil {
    213 		t.Fatalf("CloseWrite: %v", err)
    214 	}
    215 
    216 	client.SetReadDeadline(time.Now().Add(5 * time.Second))
    217 	got, err := io.ReadAll(client)
    218 	if err != nil {
    219 		t.Fatalf("reading response: %v", err)
    220 	}
    221 	if string(got) != "PONG" {
    222 		t.Fatalf("response: got %q, want %q", got, "PONG")
    223 	}
    224 }