Bläddra i källkod

Introduce bittorrent related protocol sniffers

* Introduce bittorrent related protocol sniffers

including, sniffers of
1. BitTorrent Protocol (TCP)
2. uTorrent Transport Protocol (UDP)

Signed-off-by: iosmanthus <[email protected]>
Co-authored-by: 世界 <[email protected]>
iosmanthus 1 år sedan
förälder
incheckning
4b7a83da16

+ 101 - 0
common/sniff/bittorrent.go

@@ -0,0 +1,101 @@
+package sniff
+
+import (
+	"bytes"
+	"context"
+	"encoding/binary"
+	"io"
+	"os"
+
+	"github.com/sagernet/sing-box/adapter"
+	C "github.com/sagernet/sing-box/constant"
+)
+
+const (
+	trackerConnectFlag    = 0
+	trackerProtocolID     = 0x41727101980
+	trackerConnectMinSize = 16
+)
+
+// BitTorrent detects if the stream is a BitTorrent connection.
+// For the BitTorrent protocol specification, see https://www.bittorrent.org/beps/bep_0003.html
+func BitTorrent(_ context.Context, reader io.Reader) (*adapter.InboundContext, error) {
+	var first byte
+	err := binary.Read(reader, binary.BigEndian, &first)
+	if err != nil {
+		return nil, err
+	}
+
+	if first != 19 {
+		return nil, os.ErrInvalid
+	}
+
+	var protocol [19]byte
+	_, err = reader.Read(protocol[:])
+	if err != nil {
+		return nil, err
+	}
+	if string(protocol[:]) != "BitTorrent protocol" {
+		return nil, os.ErrInvalid
+	}
+
+	return &adapter.InboundContext{
+		Protocol: C.ProtocolBitTorrent,
+	}, nil
+}
+
+// UTP detects if the packet is a uTP connection packet.
+// For the uTP protocol specification, see
+//  1. https://www.bittorrent.org/beps/bep_0029.html
+//  2. https://github.com/bittorrent/libutp/blob/2b364cbb0650bdab64a5de2abb4518f9f228ec44/utp_internal.cpp#L112
+func UTP(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
+	// A valid uTP packet must be at least 20 bytes long.
+	if len(packet) < 20 {
+		return nil, os.ErrInvalid
+	}
+
+	version := packet[0] & 0x0F
+	ty := packet[0] >> 4
+	if version != 1 || ty > 4 {
+		return nil, os.ErrInvalid
+	}
+
+	// Validate the extensions
+	extension := packet[1]
+	reader := bytes.NewReader(packet[20:])
+	for extension != 0 {
+		err := binary.Read(reader, binary.BigEndian, &extension)
+		if err != nil {
+			return nil, err
+		}
+
+		var length byte
+		err = binary.Read(reader, binary.BigEndian, &length)
+		if err != nil {
+			return nil, err
+		}
+		_, err = reader.Seek(int64(length), io.SeekCurrent)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return &adapter.InboundContext{
+		Protocol: C.ProtocolBitTorrent,
+	}, nil
+}
+
+// UDPTracker detects if the packet is a UDP Tracker Protocol packet.
+// For the UDP Tracker Protocol specification, see https://www.bittorrent.org/beps/bep_0015.html
+func UDPTracker(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
+	if len(packet) < trackerConnectMinSize {
+		return nil, os.ErrInvalid
+	}
+	if binary.BigEndian.Uint64(packet[:8]) != trackerProtocolID {
+		return nil, os.ErrInvalid
+	}
+	if binary.BigEndian.Uint32(packet[8:12]) != trackerConnectFlag {
+		return nil, os.ErrInvalid
+	}
+	return &adapter.InboundContext{Protocol: C.ProtocolBitTorrent}, nil
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 36 - 0
common/sniff/bittorrent_test.go


+ 6 - 5
constant/protocol.go

@@ -1,9 +1,10 @@
 package constant
 
 const (
-	ProtocolTLS  = "tls"
-	ProtocolHTTP = "http"
-	ProtocolQUIC = "quic"
-	ProtocolDNS  = "dns"
-	ProtocolSTUN = "stun"
+	ProtocolTLS        = "tls"
+	ProtocolHTTP       = "http"
+	ProtocolQUIC       = "quic"
+	ProtocolDNS        = "dns"
+	ProtocolSTUN       = "stun"
+	ProtocolBitTorrent = "bittorrent"
 )

+ 8 - 7
docs/configuration/route/sniff.md

@@ -2,10 +2,11 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
 
 #### Supported Protocols
 
-| Network | Protocol | Domain Name |
-|:-------:|:--------:|:-----------:|
-|   TCP   |   HTTP   |    Host     |
-|   TCP   |   TLS    | Server Name |
-|   UDP   |   QUIC   | Server Name |
-|   UDP   |   STUN   |      /      |
-| TCP/UDP |   DNS    |      /      |
+| Network |  Protocol   | Domain Name |
+|:-------:|:-----------:|:-----------:|
+|   TCP   |    HTTP     |    Host     |
+|   TCP   |     TLS     | Server Name |
+|   UDP   |    QUIC     | Server Name |
+|   UDP   |    STUN     |      /      |
+| TCP/UDP |     DNS     |      /      |
+| TCP/UDP | BitTorrent  |      /      |

+ 8 - 7
docs/configuration/route/sniff.zh.md

@@ -2,10 +2,11 @@
 
 #### 支持的协议
 
-|   网络    |  协议  |     域名      |
-|:-------:|:----:|:-----------:|
-|   TCP   | HTTP |    Host     |
-|   TCP   | TLS  | Server Name |
-|   UDP   | QUIC | Server Name |
-|   UDP   | STUN |      /      |
-| TCP/UDP | DNS  |      /      |
+|   网络    |     协议      |     域名      |
+|:-------:|:-----------:|:-----------:|
+|   TCP   |    HTTP     |    Host     |
+|   TCP   |     TLS     | Server Name |
+|   UDP   |    QUIC     | Server Name |
+|   UDP   |    STUN     |      /      |
+| TCP/UDP |     DNS     |      /      |
+| TCP/UDP | BitTorrent  |      /      |

+ 19 - 2
route/router.go

@@ -834,7 +834,16 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
 
 	if metadata.InboundOptions.SniffEnabled && !sniff.Skip(metadata) {
 		buffer := buf.NewPacket()
-		sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
+		sniffMetadata, err := sniff.PeekStream(
+			ctx,
+			conn,
+			buffer,
+			time.Duration(metadata.InboundOptions.SniffTimeout),
+			sniff.StreamDomainNameQuery,
+			sniff.TLSClientHello,
+			sniff.HTTPHost,
+			sniff.BitTorrent,
+		)
 		if sniffMetadata != nil {
 			metadata.Protocol = sniffMetadata.Protocol
 			metadata.Domain = sniffMetadata.Domain
@@ -983,7 +992,15 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
 				metadata.Destination = destination
 			}
 			if metadata.InboundOptions.SniffEnabled {
-				sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
+				sniffMetadata, _ := sniff.PeekPacket(
+				ctx,
+				buffer.Bytes(),
+				sniff.DomainNameQuery,
+				sniff.QUICClientHello,
+				sniff.STUNMessage,
+				sniff.UTP,
+				sniff.UDPTracker,
+			)
 				if sniffMetadata != nil {
 					metadata.Protocol = sniffMetadata.Protocol
 					metadata.Domain = sniffMetadata.Domain

Vissa filer visades inte eftersom för många filer har ändrats