vtun/utls/sniffer.go
NNdroid 7c8048e7c2 add utls support
add dtls support
2023-05-30 14:12:39 +08:00

132 lines
2.3 KiB
Go

package utls
import (
"bytes"
"errors"
"io"
"log"
"net"
"github.com/net-byte/vtun/common/netutil"
)
type SniffConn struct {
net.Conn
rout io.Reader
peeked, read bool
Type int
preData []byte
path string
}
const (
TypeHttp = iota
TypeHttp2
TypeUnknown
)
var (
httpMethods = [...][]byte{
[]byte("GET"),
[]byte("POST"),
[]byte("HEAD"),
[]byte("PUT"),
[]byte("DELETE"),
[]byte("OPTIONS"),
[]byte("CONNECT"),
}
http2Header = []byte("PRI * HTTP/2.0")
sep = []byte(" ")
)
func NewPeekPreDataConn(c net.Conn) *SniffConn {
s := &SniffConn{Conn: c, rout: c}
s.Type = s.sniff()
return s
}
func (c *SniffConn) peekPreData(n int) ([]byte, error) {
if c.read {
return nil, errors.New("pre-data must be peek before read")
}
if c.peeked {
return nil, errors.New("can only peek once")
}
c.peeked = true
preDate := make([]byte, n)
n, err := c.Conn.Read(preDate)
return preDate[:n], err
}
func (c *SniffConn) Read(p []byte) (int, error) {
if !c.read {
c.read = true
c.rout = io.MultiReader(bytes.NewReader(c.preData), c.Conn)
}
return c.rout.Read(p)
}
func (c *SniffConn) sniff() int {
var err error
c.preData, err = c.peekPreData(64)
if err != nil && err != io.EOF {
return TypeUnknown
}
if c.sniffHttp() {
return TypeHttp
}
if c.sniffHttp2() {
return TypeHttp2
}
return TypeUnknown
}
func (c *SniffConn) sniffHttp() bool {
preDataParts := bytes.Split(c.preData, sep)
if len(preDataParts) < 2 {
return false
}
for _, m := range httpMethods {
if bytes.Compare(preDataParts[0], m) == 0 {
c.path = string(preDataParts[1])
return true
}
}
return false
}
func (c *SniffConn) sniffHttp2() bool {
return len(c.preData) >= len(http2Header) &&
bytes.Compare(c.preData[:len(http2Header)], http2Header) == 0
}
func (c *SniffConn) SetPath(path string) {
preDataParts := bytes.Split(c.preData, sep)
preDataParts[1] = []byte(path)
c.preData = bytes.Join(preDataParts, sep)
c.path = path
}
func (c *SniffConn) GetPath() string {
return c.path
}
func (c *SniffConn) Handle() bool {
write, err := c.Write(netutil.GetDefaultHttpResponse())
if err != nil {
return false
}
defer func(c *SniffConn) {
err := c.Close()
if err != nil {
log.Printf("%x\n", err)
}
}(c)
return write > 0
}