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 }