Files
Xray-core/common/protocol/bittorrent/bittorrent.go
T

82 lines
1.6 KiB
Go

package bittorrent
import (
"encoding/binary"
"errors"
"github.com/xtls/xray-core/common"
)
type SniffHeader struct{}
func (h *SniffHeader) Protocol() string {
return "bittorrent"
}
func (h *SniffHeader) Domain() string {
return ""
}
var errNotBittorrent = errors.New("not bittorrent header")
func SniffBittorrent(b []byte) (*SniffHeader, error) {
if len(b) < 20 {
return nil, common.ErrNoClue
}
if b[0] == 19 && string(b[1:20]) == "BitTorrent protocol" {
return &SniffHeader{}, nil
}
return nil, errNotBittorrent
}
func SniffUTP(b []byte) (*SniffHeader, error) {
if len(b) < 20 {
return nil, common.ErrNoClue
}
// type 4 (ST_SYN), version 1
if b[0] != 0x41 {
return nil, errNotBittorrent
}
// timestamp_difference is always 0 in new connections
if binary.BigEndian.Uint32(b[8:12]) != 0 {
return nil, errNotBittorrent
}
// Walk the extension chain. Selective ack (1) and extension bits (2)
extension, offset := b[1], 20
for extension != 0 {
if len(b) < offset+2 {
return nil, errNotBittorrent
}
length := int(b[offset+1])
switch extension {
case 1: // selective ack
if length < 4 || length%4 != 0 {
return nil, errNotBittorrent
}
case 2: // extension bits: fixed 8 bytes, sent in ST_SYN by µTorrent
if length != 8 {
return nil, errNotBittorrent
}
default:
return nil, errNotBittorrent
}
if len(b) < offset+2+length {
return nil, errNotBittorrent
}
extension = b[offset]
offset += 2 + length
}
// extensions should consume all ST_SYN payload
if len(b) != offset {
return nil, errNotBittorrent
}
return &SniffHeader{}, nil
}