github.com/metacubex/mihomo@v1.18.5/transport/simple-obfs/http.go (about)

     1  package obfs
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"net/http"
    10  
    11  	"github.com/metacubex/mihomo/common/pool"
    12  
    13  	"github.com/zhangyunhao116/fastrand"
    14  )
    15  
    16  // HTTPObfs is shadowsocks http simple-obfs implementation
    17  type HTTPObfs struct {
    18  	net.Conn
    19  	host          string
    20  	port          string
    21  	buf           []byte
    22  	offset        int
    23  	firstRequest  bool
    24  	firstResponse bool
    25  }
    26  
    27  func (ho *HTTPObfs) Read(b []byte) (int, error) {
    28  	if ho.buf != nil {
    29  		n := copy(b, ho.buf[ho.offset:])
    30  		ho.offset += n
    31  		if ho.offset == len(ho.buf) {
    32  			pool.Put(ho.buf)
    33  			ho.buf = nil
    34  		}
    35  		return n, nil
    36  	}
    37  
    38  	if ho.firstResponse {
    39  		buf := pool.Get(pool.RelayBufferSize)
    40  		n, err := ho.Conn.Read(buf)
    41  		if err != nil {
    42  			pool.Put(buf)
    43  			return 0, err
    44  		}
    45  		idx := bytes.Index(buf[:n], []byte("\r\n\r\n"))
    46  		if idx == -1 {
    47  			pool.Put(buf)
    48  			return 0, io.EOF
    49  		}
    50  		ho.firstResponse = false
    51  		length := n - (idx + 4)
    52  		n = copy(b, buf[idx+4:n])
    53  		if length > n {
    54  			ho.buf = buf[:idx+4+length]
    55  			ho.offset = idx + 4 + n
    56  		} else {
    57  			pool.Put(buf)
    58  		}
    59  		return n, nil
    60  	}
    61  	return ho.Conn.Read(b)
    62  }
    63  
    64  func (ho *HTTPObfs) Write(b []byte) (int, error) {
    65  	if ho.firstRequest {
    66  		randBytes := make([]byte, 16)
    67  		fastrand.Read(randBytes)
    68  		req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:]))
    69  		if err != nil {
    70  			return 0, err
    71  		}
    72  		req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2))
    73  		req.Header.Set("Upgrade", "websocket")
    74  		req.Header.Set("Connection", "Upgrade")
    75  		req.Host = ho.host
    76  		if ho.port != "80" {
    77  			req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port)
    78  		}
    79  		req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes))
    80  		req.ContentLength = int64(len(b))
    81  		err = req.Write(ho.Conn)
    82  		ho.firstRequest = false
    83  		return len(b), err
    84  	}
    85  
    86  	return ho.Conn.Write(b)
    87  }
    88  
    89  // NewHTTPObfs return a HTTPObfs
    90  func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn {
    91  	return &HTTPObfs{
    92  		Conn:          conn,
    93  		firstRequest:  true,
    94  		firstResponse: true,
    95  		host:          host,
    96  		port:          port,
    97  	}
    98  }