github.com/sagernet/sing-box@v1.9.0-rc.20/transport/simple-obfs/http.go (about)

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