github.com/kelleygo/clashcore@v1.0.2/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/kelleygo/clashcore/common/utils"
    11  	C "github.com/kelleygo/clashcore/constant"
    12  	"github.com/kelleygo/clashcore/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  }