github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/net/proxy/shadowsocks/obfs_http.go (about)

     1  package shadowsocks
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"io"
     9  	"math/rand/v2"
    10  	"net"
    11  	"net/http"
    12  	"time"
    13  
    14  	"github.com/Asutorufa/yuhaiin/pkg/net/netapi"
    15  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/point"
    16  	"github.com/Asutorufa/yuhaiin/pkg/protos/node/protocol"
    17  	"github.com/Asutorufa/yuhaiin/pkg/utils/pool"
    18  )
    19  
    20  /*
    21   from https://github.com/Dreamacro/clash/blob/master/component/simple-obfs/http.go
    22  */
    23  // HTTPObfs is shadowsocks http simple-obfs implementation
    24  type HTTPObfs struct {
    25  	net.Conn
    26  	host          string
    27  	port          string
    28  	buf           []byte
    29  	offset        int
    30  	firstRequest  bool
    31  	firstResponse bool
    32  }
    33  
    34  func (ho *HTTPObfs) Read(b []byte) (int, error) {
    35  	if ho.buf != nil {
    36  		n := copy(b, ho.buf[ho.offset:])
    37  		ho.offset += n
    38  		if ho.offset == len(ho.buf) {
    39  			ho.buf = nil
    40  		}
    41  		return n, nil
    42  	}
    43  
    44  	if ho.firstResponse {
    45  		buf := pool.GetBytes(pool.DefaultSize)
    46  		defer pool.PutBytes(buf)
    47  		n, err := ho.Conn.Read(buf)
    48  		if err != nil {
    49  			// utils.BuffPool(pool.DefaultSize).Put(&(buf))
    50  			return 0, err
    51  		}
    52  		idx := bytes.Index(buf[:n], []byte("\r\n\r\n"))
    53  		if idx == -1 {
    54  			// utils.BuffPool(pool.DefaultSize).Put(&(buf))
    55  			return 0, io.EOF
    56  		}
    57  		ho.firstResponse = false
    58  		length := n - (idx + 4)
    59  		n = copy(b, buf[idx+4:n])
    60  		if length > n {
    61  			ho.buf = buf[:idx+4+length]
    62  			ho.offset = idx + 4 + n
    63  		} else {
    64  			// utils.BuffPool(pool.DefaultSize).Put(&(buf))
    65  		}
    66  		return n, nil
    67  	}
    68  	return ho.Conn.Read(b)
    69  }
    70  
    71  func (ho *HTTPObfs) Write(b []byte) (int, error) {
    72  	if ho.firstRequest {
    73  		req, _ := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:]))
    74  		req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%54, rand.Int()%2))
    75  		req.Header.Set("Upgrade", "websocket")
    76  		req.Header.Set("Connection", "Upgrade")
    77  		req.Host = fmt.Sprintf("%s:%s", ho.host, ho.port)
    78  		req.Header.Set("Sec-WebSocket-Key", base64.URLEncoding.EncodeToString([]byte(time.Now().String()[:16])))
    79  		req.ContentLength = int64(len(b))
    80  		err := req.Write(ho.Conn)
    81  		ho.firstRequest = false
    82  		return len(b), err
    83  	}
    84  
    85  	return ho.Conn.Write(b)
    86  }
    87  
    88  // newHTTPObfs return a HTTPObfs
    89  func newHTTPObfs(conn net.Conn, host string, port string) net.Conn {
    90  	return &HTTPObfs{
    91  		Conn:          conn,
    92  		firstRequest:  true,
    93  		firstResponse: true,
    94  		host:          host,
    95  		port:          port,
    96  	}
    97  }
    98  
    99  var _ netapi.Proxy = (*httpOBFS)(nil)
   100  
   101  type httpOBFS struct {
   102  	host string
   103  	port string
   104  
   105  	netapi.Proxy
   106  }
   107  
   108  func init() {
   109  	point.RegisterProtocol(NewHTTPOBFS)
   110  }
   111  
   112  func NewHTTPOBFS(config *protocol.Protocol_ObfsHttp) point.WrapProxy {
   113  	return func(p netapi.Proxy) (netapi.Proxy, error) {
   114  		return &httpOBFS{
   115  			host:  config.ObfsHttp.Host,
   116  			port:  config.ObfsHttp.Port,
   117  			Proxy: p,
   118  		}, nil
   119  	}
   120  }
   121  
   122  func (h *httpOBFS) Conn(ctx context.Context, s netapi.Address) (net.Conn, error) {
   123  	conn, err := h.Proxy.Conn(ctx, s)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return newHTTPObfs(conn, h.host, h.port), nil
   128  }