bittorrent.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package sniff
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/binary"
  6. "io"
  7. "os"
  8. "github.com/sagernet/sing-box/adapter"
  9. C "github.com/sagernet/sing-box/constant"
  10. E "github.com/sagernet/sing/common/exceptions"
  11. )
  12. const (
  13. trackerConnectFlag = 0
  14. trackerProtocolID = 0x41727101980
  15. trackerConnectMinSize = 16
  16. )
  17. // BitTorrent detects if the stream is a BitTorrent connection.
  18. // For the BitTorrent protocol specification, see https://www.bittorrent.org/beps/bep_0003.html
  19. func BitTorrent(_ context.Context, metadata *adapter.InboundContext, reader io.Reader) error {
  20. var first byte
  21. err := binary.Read(reader, binary.BigEndian, &first)
  22. if err != nil {
  23. return E.Cause1(ErrNeedMoreData, err)
  24. }
  25. if first != 19 {
  26. return os.ErrInvalid
  27. }
  28. const header = "BitTorrent protocol"
  29. var protocol [19]byte
  30. var n int
  31. n, err = reader.Read(protocol[:])
  32. if string(protocol[:n]) != header[:n] {
  33. return os.ErrInvalid
  34. }
  35. if err != nil {
  36. return E.Cause1(ErrNeedMoreData, err)
  37. }
  38. if n < 19 {
  39. return ErrNeedMoreData
  40. }
  41. metadata.Protocol = C.ProtocolBitTorrent
  42. return nil
  43. }
  44. // UTP detects if the packet is a uTP connection packet.
  45. // For the uTP protocol specification, see
  46. // 1. https://www.bittorrent.org/beps/bep_0029.html
  47. // 2. https://github.com/bittorrent/libutp/blob/2b364cbb0650bdab64a5de2abb4518f9f228ec44/utp_internal.cpp#L112
  48. func UTP(_ context.Context, metadata *adapter.InboundContext, packet []byte) error {
  49. // A valid uTP packet must be at least 20 bytes long.
  50. if len(packet) < 20 {
  51. return os.ErrInvalid
  52. }
  53. version := packet[0] & 0x0F
  54. ty := packet[0] >> 4
  55. if version != 1 || ty > 4 {
  56. return os.ErrInvalid
  57. }
  58. // Validate the extensions
  59. extension := packet[1]
  60. reader := bytes.NewReader(packet[20:])
  61. for extension != 0 {
  62. err := binary.Read(reader, binary.BigEndian, &extension)
  63. if err != nil {
  64. return err
  65. }
  66. if extension > 0x04 {
  67. return os.ErrInvalid
  68. }
  69. var length byte
  70. err = binary.Read(reader, binary.BigEndian, &length)
  71. if err != nil {
  72. return err
  73. }
  74. _, err = reader.Seek(int64(length), io.SeekCurrent)
  75. if err != nil {
  76. return err
  77. }
  78. }
  79. metadata.Protocol = C.ProtocolBitTorrent
  80. return nil
  81. }
  82. // UDPTracker detects if the packet is a UDP Tracker Protocol packet.
  83. // For the UDP Tracker Protocol specification, see https://www.bittorrent.org/beps/bep_0015.html
  84. func UDPTracker(_ context.Context, metadata *adapter.InboundContext, packet []byte) error {
  85. if len(packet) < trackerConnectMinSize {
  86. return os.ErrInvalid
  87. }
  88. if binary.BigEndian.Uint64(packet[:8]) != trackerProtocolID {
  89. return os.ErrInvalid
  90. }
  91. if binary.BigEndian.Uint32(packet[8:12]) != trackerConnectFlag {
  92. return os.ErrInvalid
  93. }
  94. metadata.Protocol = C.ProtocolBitTorrent
  95. return nil
  96. }