sniff.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package http
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "strings"
  7. "github.com/xtls/xray-core/common"
  8. "github.com/xtls/xray-core/common/net"
  9. "github.com/xtls/xray-core/common/session"
  10. )
  11. type version byte
  12. const (
  13. HTTP1 version = iota
  14. HTTP2
  15. )
  16. type SniffHeader struct {
  17. version version
  18. host string
  19. }
  20. func (h *SniffHeader) Protocol() string {
  21. switch h.version {
  22. case HTTP1:
  23. return "http1"
  24. case HTTP2:
  25. return "http2"
  26. default:
  27. return "unknown"
  28. }
  29. }
  30. func (h *SniffHeader) Domain() string {
  31. return h.host
  32. }
  33. var (
  34. methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect"}
  35. errNotHTTPMethod = errors.New("not an HTTP method")
  36. )
  37. func beginWithHTTPMethod(b []byte) error {
  38. for _, m := range &methods {
  39. if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) {
  40. return nil
  41. }
  42. if len(b) < len(m) {
  43. return common.ErrNoClue
  44. }
  45. }
  46. return errNotHTTPMethod
  47. }
  48. func SniffHTTP(b []byte, c context.Context) (*SniffHeader, error) {
  49. content := session.ContentFromContext(c)
  50. ShouldSniffAttr := true
  51. // If content.Attributes have information, that means it comes from HTTP inbound PlainHTTP mode.
  52. // It will set attributes, so skip it.
  53. if content == nil || content.AttributeLen() != 0 {
  54. ShouldSniffAttr = false
  55. }
  56. if err := beginWithHTTPMethod(b); err != nil {
  57. return nil, err
  58. }
  59. sh := &SniffHeader{
  60. version: HTTP1,
  61. }
  62. headers := bytes.Split(b, []byte{'\n'})
  63. for i := 1; i < len(headers); i++ {
  64. header := headers[i]
  65. if len(header) == 0 {
  66. break
  67. }
  68. parts := bytes.SplitN(header, []byte{':'}, 2)
  69. if len(parts) != 2 {
  70. continue
  71. }
  72. key := strings.ToLower(string(parts[0]))
  73. value := string(bytes.TrimSpace(parts[1]))
  74. if ShouldSniffAttr {
  75. content.SetAttribute(key, value) // Put header in attribute
  76. }
  77. if key == "host" {
  78. rawHost := strings.ToLower(value)
  79. dest, err := ParseHost(rawHost, net.Port(80))
  80. if err != nil {
  81. return nil, err
  82. }
  83. sh.host = dest.Address.String()
  84. }
  85. }
  86. // Parse request line
  87. // Request line is like this
  88. // "GET /homo/114514 HTTP/1.1"
  89. if len(headers) > 0 && ShouldSniffAttr {
  90. RequestLineParts := bytes.Split(headers[0], []byte{' '})
  91. if len(RequestLineParts) == 3 {
  92. content.SetAttribute(":method", string(RequestLineParts[0]))
  93. content.SetAttribute(":path", string(RequestLineParts[1]))
  94. }
  95. }
  96. if len(sh.host) > 0 {
  97. return sh, nil
  98. }
  99. return nil, common.ErrNoClue
  100. }