github.com/igoogolx/clash@v1.19.8/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 mathRand "math/rand" 10 "net" 11 "net/http" 12 13 "github.com/igoogolx/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 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 rand.Read(randBytes) 68 req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) 69 req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", mathRand.Int()%54, mathRand.Int()%2)) 70 req.Header.Set("Upgrade", "websocket") 71 req.Header.Set("Connection", "Upgrade") 72 req.Host = ho.host 73 if ho.port != "80" { 74 req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port) 75 } 76 req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes)) 77 req.ContentLength = int64(len(b)) 78 err := req.Write(ho.Conn) 79 ho.firstRequest = false 80 return len(b), err 81 } 82 83 return ho.Conn.Write(b) 84 } 85 86 // NewHTTPObfs return a HTTPObfs 87 func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn { 88 return &HTTPObfs{ 89 Conn: conn, 90 firstRequest: true, 91 firstResponse: true, 92 host: host, 93 port: port, 94 } 95 }