github.com/yaling888/clash@v1.53.0/transport/simple-obfs/http.go (about)

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