github.com/metacubex/mihomo@v1.18.5/transport/simple-obfs/http.go (about) 1 package obfs 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 11 "github.com/metacubex/mihomo/common/pool" 12 13 "github.com/zhangyunhao116/fastrand" 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 fastrand.Read(randBytes) 68 req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) 69 if err != nil { 70 return 0, err 71 } 72 req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2)) 73 req.Header.Set("Upgrade", "websocket") 74 req.Header.Set("Connection", "Upgrade") 75 req.Host = ho.host 76 if ho.port != "80" { 77 req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port) 78 } 79 req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString(randBytes)) 80 req.ContentLength = int64(len(b)) 81 err = req.Write(ho.Conn) 82 ho.firstRequest = false 83 return len(b), err 84 } 85 86 return ho.Conn.Write(b) 87 } 88 89 // NewHTTPObfs return a HTTPObfs 90 func NewHTTPObfs(conn net.Conn, host string, port string) net.Conn { 91 return &HTTPObfs{ 92 Conn: conn, 93 firstRequest: true, 94 firstResponse: true, 95 host: host, 96 port: port, 97 } 98 }