github.com/yaling888/clash@v1.53.0/transport/ssr/obfs/http_simple.go (about)

     1  package obfs
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"io"
     7  	"math/rand/v2"
     8  	"net"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/yaling888/clash/common/convert"
    13  	"github.com/yaling888/clash/common/pool"
    14  )
    15  
    16  func init() {
    17  	register("http_simple", newHTTPSimple, 0)
    18  }
    19  
    20  type httpObfs struct {
    21  	*Base
    22  	post bool
    23  }
    24  
    25  func newHTTPSimple(b *Base) Obfs {
    26  	return &httpObfs{Base: b}
    27  }
    28  
    29  type httpConn struct {
    30  	net.Conn
    31  	*httpObfs
    32  	hasSentHeader bool
    33  	hasRecvHeader bool
    34  	buf           []byte
    35  }
    36  
    37  func (h *httpObfs) StreamConn(c net.Conn) net.Conn {
    38  	return &httpConn{Conn: c, httpObfs: h}
    39  }
    40  
    41  func (c *httpConn) Read(b []byte) (int, error) {
    42  	if c.buf != nil {
    43  		n := copy(b, c.buf)
    44  		if n == len(c.buf) {
    45  			c.buf = nil
    46  		} else {
    47  			c.buf = c.buf[n:]
    48  		}
    49  		return n, nil
    50  	}
    51  
    52  	if c.hasRecvHeader {
    53  		return c.Conn.Read(b)
    54  	}
    55  
    56  	bufP := pool.GetNetBuf()
    57  	defer pool.PutNetBuf(bufP)
    58  	n, err := c.Conn.Read(*bufP)
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  	pos := bytes.Index((*bufP)[:n], []byte("\r\n\r\n"))
    63  	if pos == -1 {
    64  		return 0, io.EOF
    65  	}
    66  	c.hasRecvHeader = true
    67  	dataLength := n - pos - 4
    68  	n = copy(b, (*bufP)[4+pos:n])
    69  	if dataLength > n {
    70  		c.buf = append(c.buf, (*bufP)[4+pos+n:4+pos+dataLength]...)
    71  	}
    72  	return n, nil
    73  }
    74  
    75  func (c *httpConn) Write(b []byte) (int, error) {
    76  	if c.hasSentHeader {
    77  		return c.Conn.Write(b)
    78  	}
    79  	// 30: head length
    80  	headLength := c.IVSize + 30
    81  
    82  	bLength := len(b)
    83  	headDataLength := bLength
    84  	if bLength-headLength > 64 {
    85  		headDataLength = headLength + rand.IntN(65)
    86  	}
    87  	headData := b[:headDataLength]
    88  	b = b[headDataLength:]
    89  
    90  	var body string
    91  	host := c.Host
    92  	if len(c.Param) > 0 {
    93  		pos := strings.Index(c.Param, "#")
    94  		if pos != -1 {
    95  			body = strings.ReplaceAll(c.Param[pos+1:], "\n", "\r\n")
    96  			body = strings.ReplaceAll(body, "\\n", "\r\n")
    97  			host = c.Param[:pos]
    98  		} else {
    99  			host = c.Param
   100  		}
   101  	}
   102  	hosts := strings.Split(host, ",")
   103  	host = hosts[rand.IntN(len(hosts))]
   104  
   105  	buf := pool.GetBuffer()
   106  	defer pool.PutBuffer(buf)
   107  	if c.post {
   108  		buf.WriteString("POST /")
   109  	} else {
   110  		buf.WriteString("GET /")
   111  	}
   112  	packURLEncodedHeadData(buf, headData)
   113  	buf.WriteString(" HTTP/1.1\r\nHost: " + host)
   114  	if c.Port != 80 {
   115  		buf.WriteString(":" + strconv.Itoa(c.Port))
   116  	}
   117  	buf.WriteString("\r\n")
   118  	if len(body) > 0 {
   119  		buf.WriteString(body + "\r\n\r\n")
   120  	} else {
   121  		buf.WriteString("User-Agent: ")
   122  		buf.WriteString(convert.RandUserAgent())
   123  		buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n")
   124  		if c.post {
   125  			packBoundary(buf)
   126  		}
   127  		buf.WriteString("DNT: 1\r\nConnection: keep-alive\r\n\r\n")
   128  	}
   129  	buf.Write(b)
   130  	_, err := c.Conn.Write(buf.Bytes())
   131  	if err != nil {
   132  		return 0, nil
   133  	}
   134  	c.hasSentHeader = true
   135  	return bLength, nil
   136  }
   137  
   138  func packURLEncodedHeadData(buf *bytes.Buffer, data []byte) {
   139  	dataLength := len(data)
   140  	for i := 0; i < dataLength; i++ {
   141  		buf.WriteRune('%')
   142  		buf.WriteString(hex.EncodeToString(data[i : i+1]))
   143  	}
   144  }
   145  
   146  func packBoundary(buf *bytes.Buffer) {
   147  	buf.WriteString("Content-Type: multipart/form-data; boundary=")
   148  	set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
   149  	for i := 0; i < 32; i++ {
   150  		buf.WriteByte(set[rand.IntN(62)])
   151  	}
   152  	buf.WriteString("\r\n")
   153  }