udp: implement bep41
This commit is contained in:
parent
6260570635
commit
c667497c6d
2 changed files with 123 additions and 26 deletions
|
@ -1,8 +1,11 @@
|
|||
package udp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/chihaya/chihaya/bittorrent"
|
||||
)
|
||||
|
@ -37,11 +40,12 @@ var (
|
|||
bittorrent.Stopped,
|
||||
}
|
||||
|
||||
errMalformedPacket = bittorrent.ClientError("malformed packet")
|
||||
errMalformedIP = bittorrent.ClientError("malformed IP address")
|
||||
errMalformedEvent = bittorrent.ClientError("malformed event ID")
|
||||
errUnknownAction = bittorrent.ClientError("unknown action ID")
|
||||
errBadConnectionID = bittorrent.ClientError("bad connection ID")
|
||||
errMalformedPacket = bittorrent.ClientError("malformed packet")
|
||||
errMalformedIP = bittorrent.ClientError("malformed IP address")
|
||||
errMalformedEvent = bittorrent.ClientError("malformed event ID")
|
||||
errUnknownAction = bittorrent.ClientError("unknown action ID")
|
||||
errBadConnectionID = bittorrent.ClientError("bad connection ID")
|
||||
errUnknownOptionType = bittorrent.ClientError("unknown option type")
|
||||
)
|
||||
|
||||
// ParseAnnounce parses an AnnounceRequest from a UDP request.
|
||||
|
@ -76,7 +80,7 @@ func ParseAnnounce(r Request, allowIPSpoofing bool) (*bittorrent.AnnounceRequest
|
|||
numWant := binary.BigEndian.Uint32(r.Packet[92:96])
|
||||
port := binary.BigEndian.Uint16(r.Packet[96:98])
|
||||
|
||||
params, err := handleOptionalParameters(r.Packet)
|
||||
params, err := handleOptionalParameters(r.Packet[98:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -97,43 +101,65 @@ func ParseAnnounce(r Request, allowIPSpoofing bool) (*bittorrent.AnnounceRequest
|
|||
}, nil
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
var bufferFree = sync.Pool{
|
||||
New: func() interface{} { return new(buffer) },
|
||||
}
|
||||
|
||||
func newBuffer() *buffer {
|
||||
return bufferFree.Get().(*buffer)
|
||||
}
|
||||
|
||||
func (b *buffer) free() {
|
||||
b.Reset()
|
||||
bufferFree.Put(b)
|
||||
}
|
||||
|
||||
// handleOptionalParameters parses the optional parameters as described in BEP
|
||||
// 41 and updates an announce with the values parsed.
|
||||
func handleOptionalParameters(packet []byte) (params bittorrent.Params, err error) {
|
||||
if len(packet) <= 98 {
|
||||
return
|
||||
func handleOptionalParameters(packet []byte) (bittorrent.Params, error) {
|
||||
if len(packet) == 0 {
|
||||
return bittorrent.ParseURLData("")
|
||||
}
|
||||
|
||||
optionStartIndex := 98
|
||||
for optionStartIndex < len(packet)-1 {
|
||||
option := packet[optionStartIndex]
|
||||
var buf = newBuffer()
|
||||
defer buf.free()
|
||||
|
||||
for i := 0; i < len(packet); {
|
||||
option := packet[i]
|
||||
switch option {
|
||||
case optionEndOfOptions:
|
||||
return
|
||||
|
||||
return bittorrent.ParseURLData(buf.String())
|
||||
case optionNOP:
|
||||
optionStartIndex++
|
||||
|
||||
i++
|
||||
case optionURLData:
|
||||
if optionStartIndex+1 > len(packet)-1 {
|
||||
return params, errMalformedPacket
|
||||
if i+1 >= len(packet) {
|
||||
return nil, errMalformedPacket
|
||||
}
|
||||
|
||||
length := int(packet[optionStartIndex+1])
|
||||
if optionStartIndex+1+length > len(packet)-1 {
|
||||
return params, errMalformedPacket
|
||||
length := int(packet[i+1])
|
||||
if i+2+length > len(packet) {
|
||||
return nil, errMalformedPacket
|
||||
}
|
||||
|
||||
// TODO(chihaya): Actually parse the URL Data as described in BEP 41
|
||||
// into something that fulfills the bittorrent.Params interface.
|
||||
n, err := buf.Write(packet[i+2 : i+2+length])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != length {
|
||||
return nil, fmt.Errorf("expected to write %d bytes, wrote %d", length, n)
|
||||
}
|
||||
|
||||
optionStartIndex += 1 + length
|
||||
i += 2 + length
|
||||
default:
|
||||
return
|
||||
return nil, errUnknownOptionType
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return bittorrent.ParseURLData(buf.String())
|
||||
}
|
||||
|
||||
// ParseScrape parses a ScrapeRequest from a UDP request.
|
||||
|
|
71
frontend/udp/parser_test.go
Normal file
71
frontend/udp/parser_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package udp
|
||||
|
||||
import "testing"
|
||||
|
||||
var table = []struct {
|
||||
data []byte
|
||||
values map[string]string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
[]byte{0x2, 0x5, '/', '?', 'a', '=', 'b'},
|
||||
map[string]string{"a": "b"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x2, 0x0},
|
||||
map[string]string{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x2, 0x1},
|
||||
nil,
|
||||
errMalformedPacket,
|
||||
},
|
||||
{
|
||||
[]byte{0x2},
|
||||
nil,
|
||||
errMalformedPacket,
|
||||
},
|
||||
{
|
||||
[]byte{0x2, 0x8, '/', 'c', '/', 'd', '?', 'a', '=', 'b'},
|
||||
map[string]string{"a": "b"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x2, 0x2, '/', '?', 0x2, 0x3, 'a', '=', 'b'},
|
||||
map[string]string{"a": "b"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]byte{0x2, 0x9, '/', '?', 'a', '=', 'b', '%', '2', '0', 'c'},
|
||||
map[string]string{"a": "b c"},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
func TestHandleOptionalParameters(t *testing.T) {
|
||||
for _, testCase := range table {
|
||||
params, err := handleOptionalParameters(testCase.data)
|
||||
if err != testCase.err {
|
||||
if testCase.err == nil {
|
||||
t.Fatalf("expected no parsing error for %x but got %s", testCase.data, err)
|
||||
} else {
|
||||
t.Fatalf("expected parsing error for %x", testCase.data)
|
||||
}
|
||||
}
|
||||
if testCase.values != nil {
|
||||
if params == nil {
|
||||
t.Fatalf("expected values %v for %x", testCase.values, testCase.data)
|
||||
} else {
|
||||
for key, want := range testCase.values {
|
||||
if got, ok := params.String(key); !ok {
|
||||
t.Fatalf("params missing entry %s for data %x", key, testCase.data)
|
||||
} else if got != want {
|
||||
t.Fatalf("expected param %s=%s, but was %s for data %x", key, want, got, testCase.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue