bittorrent.go 2.4 KB

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