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 }