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 }