mic.git


mic

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

proxy.go (3580B)

      1 package proxy
      2 
      3 import (
      4 	"bufio"
      5 	"crypto/x509"
      6 	"errors"
      7 	"fmt"
      8 	"io"
      9 	"log"
     10 	"net"
     11 	"sync"
     12 	"time"
     13 
     14 	utls "github.com/bogdanfinn/utls"
     15 	"github.com/djnnvx/mic/fingerprint"
     16 )
     17 
     18 const (
     19 	dialTimeout         = 10 * time.Second
     20 	acceptRetryDelay    = 5 * time.Millisecond
     21 	acceptMaxRetryDelay = 1 * time.Second
     22 )
     23 
     24 type Proxy struct {
     25 	ListenAddr  string
     26 	BackendAddr string // server-front: backend host:port
     27 	CAPool      *x509.CertPool
     28 	Fingerprint *fingerprint.TLSFingerprint
     29 	LocalCA     *LocalCA // client-front: MitM CA for TLS interception
     30 	Handler     Handler
     31 }
     32 
     33 type Handler func(conn net.Conn, p *Proxy)
     34 
     35 func (p *Proxy) dialTarget(host string) (*utls.UConn, error) {
     36 	tcpConn, err := net.DialTimeout("tcp", host, dialTimeout)
     37 	if err != nil {
     38 		return nil, err
     39 	}
     40 
     41 	helloID := utls.HelloRandomized
     42 	if p.Fingerprint != nil {
     43 		helloID = p.Fingerprint.ClientHelloID()
     44 	}
     45 
     46 	serverName, _, err := net.SplitHostPort(host)
     47 	if err != nil {
     48 		tcpConn.Close()
     49 		return nil, fmt.Errorf("dialTarget: parse host %q: %w", host, err)
     50 	}
     51 
     52 	cfg := &utls.Config{ServerName: serverName}
     53 	if p.CAPool != nil {
     54 		cfg.RootCAs = p.CAPool
     55 	}
     56 
     57 	uconn := utls.UClient(tcpConn, cfg, helloID, false, false, false)
     58 	if err := uconn.Handshake(); err != nil {
     59 		uconn.Close()
     60 		return nil, err
     61 	}
     62 	return uconn, nil
     63 }
     64 
     65 // bufferedConn reads from a bufio.Reader that may already hold bytes read ahead
     66 // from the conn (e.g. by http.ReadRequest). Writes and Close go to the conn.
     67 type bufferedConn struct {
     68 	r *bufio.Reader
     69 	net.Conn
     70 }
     71 
     72 func (b *bufferedConn) Read(p []byte) (int, error) { return b.r.Read(p) }
     73 
     74 func (b *bufferedConn) CloseWrite() error {
     75 	if hc, ok := b.Conn.(halfCloser); ok {
     76 		return hc.CloseWrite()
     77 	}
     78 	return errNoHalfClose
     79 }
     80 
     81 var errNoHalfClose = errors.New("proxy: connection does not support half-close")
     82 
     83 // halfCloser is implemented by *net.TCPConn, *tls.Conn and *utls.UConn.
     84 type halfCloser interface {
     85 	CloseWrite() error
     86 }
     87 
     88 // closeWrite shuts down only the write side so the peer still receives the data
     89 // already sent.
     90 func closeWrite(c io.Closer) {
     91 	if hc, ok := c.(halfCloser); ok && hc.CloseWrite() == nil {
     92 		return
     93 	}
     94 	c.Close()
     95 }
     96 
     97 // pipe copies both directions. A finished direction only half-closes its write
     98 // side: a client that shut its write end after sending a request must still
     99 // receive the response.
    100 func pipe(a, b io.ReadWriteCloser) {
    101 	var wg sync.WaitGroup
    102 	wg.Add(2)
    103 
    104 	go func() {
    105 		defer wg.Done()
    106 		io.Copy(b, a)
    107 		closeWrite(b)
    108 	}()
    109 
    110 	go func() {
    111 		defer wg.Done()
    112 		io.Copy(a, b)
    113 		closeWrite(a)
    114 	}()
    115 
    116 	wg.Wait()
    117 	a.Close()
    118 	b.Close()
    119 }
    120 
    121 func (p *Proxy) Run() error {
    122 	if p.Handler == nil {
    123 		return fmt.Errorf("proxy: Handler is required")
    124 	}
    125 	ln, err := net.Listen("tcp", p.ListenAddr)
    126 	if err != nil {
    127 		return err
    128 	}
    129 	defer ln.Close()
    130 
    131 	log.Printf("[+] Starting proxy on %s", p.ListenAddr)
    132 	return p.Serve(ln)
    133 }
    134 
    135 // Serve accepts connections until the listener is closed. Transient accept
    136 // errors are retried with exponential backoff, mirroring net/http.Server.Serve.
    137 func (p *Proxy) Serve(ln net.Listener) error {
    138 	if p.Handler == nil {
    139 		return fmt.Errorf("proxy: Handler is required")
    140 	}
    141 
    142 	var delay time.Duration
    143 	for {
    144 		conn, err := ln.Accept()
    145 		if err != nil {
    146 			if errors.Is(err, net.ErrClosed) {
    147 				return err
    148 			}
    149 			if delay == 0 {
    150 				delay = acceptRetryDelay
    151 			} else {
    152 				delay *= 2
    153 			}
    154 			if delay > acceptMaxRetryDelay {
    155 				delay = acceptMaxRetryDelay
    156 			}
    157 			log.Printf("Failed to accept connection: %v; retrying in %v", err, delay)
    158 			time.Sleep(delay)
    159 			continue
    160 		}
    161 		delay = 0
    162 		go p.Handler(conn, p)
    163 	}
    164 }