github.com/metacubex/mihomo@v1.18.5/component/sniffer/http_sniffer.go (about) 1 package sniffer 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "net" 8 "strings" 9 10 "github.com/metacubex/mihomo/common/utils" 11 C "github.com/metacubex/mihomo/constant" 12 "github.com/metacubex/mihomo/constant/sniffer" 13 ) 14 15 var ( 16 // refer to https://pkg.go.dev/net/http@master#pkg-constants 17 methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect", "patch", "trace"} 18 errNotHTTPMethod = errors.New("not an HTTP method") 19 ) 20 21 type version byte 22 23 const ( 24 HTTP1 version = iota 25 HTTP2 26 ) 27 28 type HTTPSniffer struct { 29 *BaseSniffer 30 version version 31 host string 32 } 33 34 var _ sniffer.Sniffer = (*HTTPSniffer)(nil) 35 36 func NewHTTPSniffer(snifferConfig SnifferConfig) (*HTTPSniffer, error) { 37 ports := snifferConfig.Ports 38 if len(ports) == 0 { 39 ports = utils.IntRanges[uint16]{utils.NewRange[uint16](80, 80)} 40 } 41 return &HTTPSniffer{ 42 BaseSniffer: NewBaseSniffer(ports, C.TCP), 43 }, nil 44 } 45 46 func (http *HTTPSniffer) Protocol() string { 47 switch http.version { 48 case HTTP1: 49 return "http1" 50 case HTTP2: 51 return "http2" 52 default: 53 return "unknown" 54 } 55 } 56 57 func (http *HTTPSniffer) SupportNetwork() C.NetWork { 58 return C.TCP 59 } 60 61 func (http *HTTPSniffer) SniffData(bytes []byte) (string, error) { 62 domain, err := SniffHTTP(bytes) 63 if err == nil { 64 return *domain, nil 65 } else { 66 return "", err 67 } 68 } 69 70 func beginWithHTTPMethod(b []byte) error { 71 for _, m := range &methods { 72 if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) { 73 return nil 74 } 75 76 if len(b) < len(m) { 77 return ErrNoClue 78 } 79 } 80 return errNotHTTPMethod 81 } 82 83 func SniffHTTP(b []byte) (*string, error) { 84 if err := beginWithHTTPMethod(b); err != nil { 85 return nil, err 86 } 87 88 _ = &HTTPSniffer{ 89 version: HTTP1, 90 } 91 92 headers := bytes.Split(b, []byte{'\n'}) 93 for i := 1; i < len(headers); i++ { 94 header := headers[i] 95 if len(header) == 0 { 96 break 97 } 98 parts := bytes.SplitN(header, []byte{':'}, 2) 99 if len(parts) != 2 { 100 continue 101 } 102 key := strings.ToLower(string(parts[0])) 103 if key == "host" { 104 rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) 105 host, _, err := net.SplitHostPort(rawHost) 106 if err != nil { 107 if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") { 108 return parseHost(rawHost) 109 } else { 110 return nil, err 111 } 112 } 113 114 if net.ParseIP(host) != nil { 115 return nil, fmt.Errorf("host is ip") 116 } 117 118 return &host, nil 119 } 120 } 121 return nil, ErrNoClue 122 } 123 124 func parseHost(host string) (*string, error) { 125 if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { 126 if net.ParseIP(host[1:len(host)-1]) != nil { 127 return nil, fmt.Errorf("host is ip") 128 } 129 } 130 131 if net.ParseIP(host) != nil { 132 return nil, fmt.Errorf("host is ip") 133 } 134 135 return &host, nil 136 }