github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/shadowsocksr/obfs/http_simple.go (about) 1 package obfs 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "fmt" 7 "io" 8 "math/rand/v2" 9 "net" 10 "strings" 11 12 "github.com/Asutorufa/yuhaiin/pkg/utils/pool" 13 ) 14 15 var ( 16 requestPath = []string{ 17 "", "", 18 "login.php?redir=", "", 19 "register.php?code=", "", 20 "?keyword=", "", 21 "search?src=typd&q=", "&lang=en", 22 "s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&bar=&wd=", "&rn=", 23 "post.php?id=", "&goto=view.php", 24 } 25 requestUserAgent = []string{ 26 "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", 27 "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", 28 "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", 29 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", 30 "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", 31 "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", 32 "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", 33 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", 34 "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", 35 "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", 36 "Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", 37 "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", 38 } 39 ) 40 41 // HttpSimple http_simple obfs encapsulate 42 type httpSimplePost struct { 43 Obfs 44 rawTransSent bool 45 rawTransReceived bool 46 userAgentIndex int 47 methodGet bool // true for get, false for post 48 49 buf []byte 50 // wbuf [pool.DefaultSize / 4]byte 51 net.Conn 52 53 param simpleParam 54 } 55 56 // newHttpSimple create a http_simple object 57 func newHttpSimple(conn net.Conn, info Obfs) net.Conn { 58 t := &httpSimplePost{ 59 userAgentIndex: rand.IntN(len(requestUserAgent)), 60 methodGet: true, 61 Conn: conn, 62 Obfs: info, 63 param: simpleParam{}, 64 } 65 66 t.param.parse(t.Param) 67 return t 68 } 69 70 func (t *httpSimplePost) boundary() (ret string) { 71 set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 72 for i := 0; i < 32; i++ { 73 ret = fmt.Sprintf("%s%c", ret, set[rand.IntN(len(set))]) 74 } 75 return 76 } 77 78 func (t *httpSimplePost) data2URLEncode(data []byte) (ret string) { 79 for i := 0; i < len(data); i++ { 80 ret = fmt.Sprintf("%s%%%s", ret, hex.EncodeToString([]byte{data[i]})) 81 } 82 return 83 } 84 85 type simpleParam struct { 86 customHead string 87 hosts []string 88 } 89 90 func (s *simpleParam) parse(param string) { 91 if len(param) == 0 { 92 return 93 } 94 95 customHeads := strings.Split(param, "#") 96 if len(customHeads) > 1 { 97 s.customHead = strings.Replace(customHeads[1], "\\n", "\r\n", -1) 98 param = customHeads[0] 99 } 100 101 s.hosts = strings.Split(param, ",") 102 } 103 104 func (s *simpleParam) getRandHost(host string) string { 105 if len(s.hosts) == 0 { 106 return host 107 } 108 return s.hosts[rand.IntN(len(s.hosts))] 109 } 110 111 func (t *httpSimplePost) encode(data []byte) []byte { 112 if t.rawTransSent { 113 return data 114 } 115 116 dataLength := len(data) 117 headSize := t.IVSize() + 30 118 if dataLength-headSize > 64 { 119 headSize = headSize + rand.IntN(64) 120 } else { 121 headSize = dataLength 122 } 123 124 buf := pool.GetBuffer() 125 defer pool.PutBuffer(buf) 126 127 if t.methodGet { 128 buf.WriteString("GET /") 129 } else { 130 buf.WriteString("POST /") 131 } 132 133 randPathIndex := rand.IntN(len(requestPath)/2) * 2 134 135 buf.WriteString(requestPath[randPathIndex]) 136 buf.WriteString(t.data2URLEncode(data[:headSize])) 137 buf.WriteString(requestPath[randPathIndex+1]) 138 buf.WriteString("HTTP/1.1\r\n") 139 buf.WriteString(fmt.Sprintf("Host: %s\r\n", net.JoinHostPort(t.param.getRandHost(t.Host), t.Port))) 140 141 if len(t.param.customHead) > 0 { 142 buf.WriteString(t.param.customHead) 143 buf.WriteString("\r\n\r\n") 144 } else { 145 buf.WriteString(fmt.Sprintf("User-Agent: %s\r\n", requestUserAgent[t.userAgentIndex])) 146 buf.WriteString("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n") 147 buf.WriteString("Accept-Language: en-US,en;q=0.8\r\n") 148 buf.WriteString("Accept-Encoding: gzip, deflate\r\n") 149 if !t.methodGet { 150 buf.WriteString(fmt.Sprintf("Content-Type: multipart/form-data; boundary=%s\r\n", t.boundary())) 151 } 152 buf.WriteString("DNT: 1\r\n") 153 buf.WriteString("Connection: keep-alive\r\n") 154 buf.WriteString("\r\n") 155 } 156 buf.Write(data[headSize:]) 157 158 t.rawTransSent = true 159 160 return buf.Bytes() 161 } 162 163 func (t *httpSimplePost) Read(b []byte) (int, error) { 164 if t.buf != nil { 165 n := copy(b, t.buf) 166 if n == len(t.buf) { 167 t.buf = nil 168 } else { 169 t.buf = t.buf[n:] 170 } 171 return n, nil 172 } 173 174 if t.rawTransReceived { 175 return t.Conn.Read(b) 176 } 177 178 buf := pool.GetBytes(pool.DefaultSize) 179 defer pool.PutBytes(buf) 180 181 n, err := t.Conn.Read(buf) 182 if err != nil { 183 return n, fmt.Errorf("read http simple header failed: %w", err) 184 } 185 186 pos := bytes.Index(buf[:n], []byte("\r\n\r\n")) 187 if pos == -1 { 188 return 0, io.EOF 189 } 190 pos = pos + 4 191 192 nn := copy(b, buf[pos:n]) 193 if n-pos-4 > nn { 194 t.buf = append(t.buf, buf[pos+nn:n]...) 195 } 196 197 t.rawTransReceived = true 198 199 return nn, nil 200 } 201 202 func (t *httpSimplePost) Write(b []byte) (int, error) { 203 if t.rawTransSent { 204 return t.Conn.Write(b) 205 } 206 207 _, err := t.Conn.Write(t.encode(b)) 208 return len(b), err 209 } 210 211 func (t *httpSimplePost) GetOverhead() int { return 0 }