init
This commit is contained in:
22
vendor/github.com/anacrolix/torrent/peer_protocol/compactip.go
generated
vendored
Normal file
22
vendor/github.com/anacrolix/torrent/peer_protocol/compactip.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package peer_protocol
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
// Marshals to the smallest compact byte representation.
|
||||
type CompactIp net.IP
|
||||
|
||||
var _ bencode.Marshaler = CompactIp{}
|
||||
|
||||
func (me CompactIp) MarshalBencode() ([]byte, error) {
|
||||
return bencode.Marshal(func() []byte {
|
||||
if ip4 := net.IP(me).To4(); ip4 != nil {
|
||||
return ip4
|
||||
} else {
|
||||
return me
|
||||
}
|
||||
}())
|
||||
}
|
124
vendor/github.com/anacrolix/torrent/peer_protocol/decoder.go
generated
vendored
Normal file
124
vendor/github.com/anacrolix/torrent/peer_protocol/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package peer_protocol
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
R *bufio.Reader
|
||||
Pool *sync.Pool
|
||||
MaxLength Integer // TODO: Should this include the length header or not?
|
||||
}
|
||||
|
||||
// io.EOF is returned if the source terminates cleanly on a message boundary.
|
||||
// TODO: Is that before or after the message?
|
||||
func (d *Decoder) Decode(msg *Message) (err error) {
|
||||
var length Integer
|
||||
err = binary.Read(d.R, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
err = fmt.Errorf("error reading message length: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if length > d.MaxLength {
|
||||
return errors.New("message too long")
|
||||
}
|
||||
if length == 0 {
|
||||
msg.Keepalive = true
|
||||
return
|
||||
}
|
||||
msg.Keepalive = false
|
||||
r := &io.LimitedReader{R: d.R, N: int64(length)}
|
||||
// Check that all of r was utilized.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.N != 0 {
|
||||
err = fmt.Errorf("%d bytes unused in message type %d", r.N, msg.Type)
|
||||
}
|
||||
}()
|
||||
msg.Keepalive = false
|
||||
c, err := readByte(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msg.Type = MessageType(c)
|
||||
switch msg.Type {
|
||||
case Choke, Unchoke, Interested, NotInterested, HaveAll, HaveNone:
|
||||
return
|
||||
case Have, AllowedFast, Suggest:
|
||||
err = msg.Index.Read(r)
|
||||
case Request, Cancel, Reject:
|
||||
for _, data := range []*Integer{&msg.Index, &msg.Begin, &msg.Length} {
|
||||
err = data.Read(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
case Bitfield:
|
||||
b := make([]byte, length-1)
|
||||
_, err = io.ReadFull(r, b)
|
||||
msg.Bitfield = unmarshalBitfield(b)
|
||||
case Piece:
|
||||
for _, pi := range []*Integer{&msg.Index, &msg.Begin} {
|
||||
err := pi.Read(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dataLen := r.N
|
||||
msg.Piece = (*d.Pool.Get().(*[]byte))
|
||||
if int64(cap(msg.Piece)) < dataLen {
|
||||
return errors.New("piece data longer than expected")
|
||||
}
|
||||
msg.Piece = msg.Piece[:dataLen]
|
||||
_, err := io.ReadFull(r, msg.Piece)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "reading piece data")
|
||||
}
|
||||
case Extended:
|
||||
b, err := readByte(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
msg.ExtendedID = ExtensionNumber(b)
|
||||
msg.ExtendedPayload, err = ioutil.ReadAll(r)
|
||||
case Port:
|
||||
err = binary.Read(r, binary.BigEndian, &msg.Port)
|
||||
default:
|
||||
err = fmt.Errorf("unknown message type %#v", c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readByte(r io.Reader) (b byte, err error) {
|
||||
var arr [1]byte
|
||||
n, err := r.Read(arr[:])
|
||||
b = arr[0]
|
||||
if n == 1 {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func unmarshalBitfield(b []byte) (bf []bool) {
|
||||
for _, c := range b {
|
||||
for i := 7; i >= 0; i-- {
|
||||
bf = append(bf, (c>>uint(i))&1 == 1)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
32
vendor/github.com/anacrolix/torrent/peer_protocol/extended.go
generated
vendored
Normal file
32
vendor/github.com/anacrolix/torrent/peer_protocol/extended.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package peer_protocol
|
||||
|
||||
import "net"
|
||||
|
||||
// http://www.bittorrent.org/beps/bep_0010.html
|
||||
type (
|
||||
ExtendedHandshakeMessage struct {
|
||||
M map[ExtensionName]ExtensionNumber `bencode:"m"`
|
||||
V string `bencode:"v,omitempty"`
|
||||
Reqq int `bencode:"reqq,omitempty"`
|
||||
Encryption bool `bencode:"e,omitempty"`
|
||||
// BEP 9
|
||||
MetadataSize int `bencode:"metadata_size,omitempty"`
|
||||
// The local client port. It would be redundant for the receiving side of
|
||||
// a connection to send this.
|
||||
Port int `bencode:"p,omitempty"`
|
||||
YourIp CompactIp `bencode:"yourip,omitempty"`
|
||||
Ipv4 CompactIp `bencode:"ipv4,omitempty"`
|
||||
Ipv6 net.IP `bencode:"ipv6,omitempty"`
|
||||
}
|
||||
|
||||
ExtensionName string
|
||||
ExtensionNumber int
|
||||
)
|
||||
|
||||
const (
|
||||
// http://www.bittorrent.org/beps/bep_0011.html
|
||||
ExtensionNamePex ExtensionName = "ut_pex"
|
||||
// http://bittorrent.org/beps/bep_0009.html. Note that there's an
|
||||
// LT_metadata, but I've never implemented it.
|
||||
ExtensionNameMetadata = "ut_metadata"
|
||||
)
|
138
vendor/github.com/anacrolix/torrent/peer_protocol/handshake.go
generated
vendored
Normal file
138
vendor/github.com/anacrolix/torrent/peer_protocol/handshake.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
package peer_protocol
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/anacrolix/missinggo"
|
||||
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
type ExtensionBit uint
|
||||
|
||||
const (
|
||||
ExtensionBitDHT = 0 // http://www.bittorrent.org/beps/bep_0005.html
|
||||
ExtensionBitExtended = 20 // http://www.bittorrent.org/beps/bep_0010.html
|
||||
ExtensionBitFast = 2 // http://www.bittorrent.org/beps/bep_0006.html
|
||||
)
|
||||
|
||||
func handshakeWriter(w io.Writer, bb <-chan []byte, done chan<- error) {
|
||||
var err error
|
||||
for b := range bb {
|
||||
_, err = w.Write(b)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
done <- err
|
||||
}
|
||||
|
||||
type (
|
||||
PeerExtensionBits [8]byte
|
||||
)
|
||||
|
||||
func (me PeerExtensionBits) String() string {
|
||||
return hex.EncodeToString(me[:])
|
||||
}
|
||||
|
||||
func NewPeerExtensionBytes(bits ...ExtensionBit) (ret PeerExtensionBits) {
|
||||
for _, b := range bits {
|
||||
ret.SetBit(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pex PeerExtensionBits) SupportsExtended() bool {
|
||||
return pex.GetBit(ExtensionBitExtended)
|
||||
}
|
||||
|
||||
func (pex PeerExtensionBits) SupportsDHT() bool {
|
||||
return pex.GetBit(ExtensionBitDHT)
|
||||
}
|
||||
|
||||
func (pex PeerExtensionBits) SupportsFast() bool {
|
||||
return pex.GetBit(ExtensionBitFast)
|
||||
}
|
||||
|
||||
func (pex *PeerExtensionBits) SetBit(bit ExtensionBit) {
|
||||
pex[7-bit/8] |= 1 << (bit % 8)
|
||||
}
|
||||
|
||||
func (pex PeerExtensionBits) GetBit(bit ExtensionBit) bool {
|
||||
return pex[7-bit/8]&(1<<(bit%8)) != 0
|
||||
}
|
||||
|
||||
type HandshakeResult struct {
|
||||
PeerExtensionBits
|
||||
PeerID [20]byte
|
||||
metainfo.Hash
|
||||
}
|
||||
|
||||
// ih is nil if we expect the peer to declare the InfoHash, such as when the
|
||||
// peer initiated the connection. Returns ok if the Handshake was successful,
|
||||
// and err if there was an unexpected condition other than the peer simply
|
||||
// abandoning the Handshake.
|
||||
func Handshake(sock io.ReadWriter, ih *metainfo.Hash, peerID [20]byte, extensions PeerExtensionBits) (res HandshakeResult, ok bool, err error) {
|
||||
// Bytes to be sent to the peer. Should never block the sender.
|
||||
postCh := make(chan []byte, 4)
|
||||
// A single error value sent when the writer completes.
|
||||
writeDone := make(chan error, 1)
|
||||
// Performs writes to the socket and ensures posts don't block.
|
||||
go handshakeWriter(sock, postCh, writeDone)
|
||||
|
||||
defer func() {
|
||||
close(postCh) // Done writing.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Wait until writes complete before returning from handshake.
|
||||
err = <-writeDone
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error writing: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
post := func(bb []byte) {
|
||||
select {
|
||||
case postCh <- bb:
|
||||
default:
|
||||
panic("mustn't block while posting")
|
||||
}
|
||||
}
|
||||
|
||||
post([]byte(Protocol))
|
||||
post(extensions[:])
|
||||
if ih != nil { // We already know what we want.
|
||||
post(ih[:])
|
||||
post(peerID[:])
|
||||
}
|
||||
var b [68]byte
|
||||
_, err = io.ReadFull(sock, b[:68])
|
||||
if err != nil {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
if string(b[:20]) != Protocol {
|
||||
return
|
||||
}
|
||||
missinggo.CopyExact(&res.PeerExtensionBits, b[20:28])
|
||||
missinggo.CopyExact(&res.Hash, b[28:48])
|
||||
missinggo.CopyExact(&res.PeerID, b[48:68])
|
||||
// peerExtensions.Add(res.PeerExtensionBits.String(), 1)
|
||||
|
||||
// TODO: Maybe we can just drop peers here if we're not interested. This
|
||||
// could prevent them trying to reconnect, falsely believing there was
|
||||
// just a problem.
|
||||
if ih == nil { // We were waiting for the peer to tell us what they wanted.
|
||||
post(res.Hash[:])
|
||||
post(peerID[:])
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
25
vendor/github.com/anacrolix/torrent/peer_protocol/int.go
generated
vendored
Normal file
25
vendor/github.com/anacrolix/torrent/peer_protocol/int.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package peer_protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Integer uint32
|
||||
|
||||
func (i *Integer) Read(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, i)
|
||||
}
|
||||
|
||||
// It's perfectly fine to cast these to an int. TODO: Or is it?
|
||||
func (i Integer) Int() int {
|
||||
return int(i)
|
||||
}
|
||||
|
||||
func (i Integer) Uint64() uint64 {
|
||||
return uint64(i)
|
||||
}
|
||||
|
||||
func (i Integer) Uint32() uint32 {
|
||||
return uint32(i)
|
||||
}
|
30
vendor/github.com/anacrolix/torrent/peer_protocol/messagetype_string.go
generated
vendored
Normal file
30
vendor/github.com/anacrolix/torrent/peer_protocol/messagetype_string.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Code generated by "stringer -type=MessageType"; DO NOT EDIT.
|
||||
|
||||
package peer_protocol
|
||||
|
||||
import "strconv"
|
||||
|
||||
const (
|
||||
_MessageType_name_0 = "ChokeUnchokeInterestedNotInterestedHaveBitfieldRequestPieceCancelPort"
|
||||
_MessageType_name_1 = "SuggestHaveAllHaveNoneRejectAllowedFast"
|
||||
_MessageType_name_2 = "Extended"
|
||||
)
|
||||
|
||||
var (
|
||||
_MessageType_index_0 = [...]uint8{0, 5, 12, 22, 35, 39, 47, 54, 59, 65, 69}
|
||||
_MessageType_index_1 = [...]uint8{0, 7, 14, 22, 28, 39}
|
||||
)
|
||||
|
||||
func (i MessageType) String() string {
|
||||
switch {
|
||||
case 0 <= i && i <= 9:
|
||||
return _MessageType_name_0[_MessageType_index_0[i]:_MessageType_index_0[i+1]]
|
||||
case 13 <= i && i <= 17:
|
||||
i -= 13
|
||||
return _MessageType_name_1[_MessageType_index_1[i]:_MessageType_index_1[i+1]]
|
||||
case i == 20:
|
||||
return _MessageType_name_2
|
||||
default:
|
||||
return "MessageType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
116
vendor/github.com/anacrolix/torrent/peer_protocol/msg.go
generated
vendored
Normal file
116
vendor/github.com/anacrolix/torrent/peer_protocol/msg.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package peer_protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Keepalive bool
|
||||
Type MessageType
|
||||
Index, Begin, Length Integer
|
||||
Piece []byte
|
||||
Bitfield []bool
|
||||
ExtendedID ExtensionNumber
|
||||
ExtendedPayload []byte
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func MakeCancelMessage(piece, offset, length Integer) Message {
|
||||
return Message{
|
||||
Type: Cancel,
|
||||
Index: piece,
|
||||
Begin: offset,
|
||||
Length: length,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg Message) RequestSpec() (ret RequestSpec) {
|
||||
return RequestSpec{
|
||||
msg.Index,
|
||||
msg.Begin,
|
||||
func() Integer {
|
||||
if msg.Type == Piece {
|
||||
return Integer(len(msg.Piece))
|
||||
} else {
|
||||
return msg.Length
|
||||
}
|
||||
}(),
|
||||
}
|
||||
}
|
||||
|
||||
func (msg Message) MustMarshalBinary() []byte {
|
||||
b, err := msg.MarshalBinary()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (msg Message) MarshalBinary() (data []byte, err error) {
|
||||
buf := &bytes.Buffer{}
|
||||
if !msg.Keepalive {
|
||||
err = buf.WriteByte(byte(msg.Type))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch msg.Type {
|
||||
case Choke, Unchoke, Interested, NotInterested, HaveAll, HaveNone:
|
||||
case Have:
|
||||
err = binary.Write(buf, binary.BigEndian, msg.Index)
|
||||
case Request, Cancel, Reject:
|
||||
for _, i := range []Integer{msg.Index, msg.Begin, msg.Length} {
|
||||
err = binary.Write(buf, binary.BigEndian, i)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
case Bitfield:
|
||||
_, err = buf.Write(marshalBitfield(msg.Bitfield))
|
||||
case Piece:
|
||||
for _, i := range []Integer{msg.Index, msg.Begin} {
|
||||
err = binary.Write(buf, binary.BigEndian, i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
n, err := buf.Write(msg.Piece)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if n != len(msg.Piece) {
|
||||
panic(n)
|
||||
}
|
||||
case Extended:
|
||||
err = buf.WriteByte(byte(msg.ExtendedID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buf.Write(msg.ExtendedPayload)
|
||||
case Port:
|
||||
err = binary.Write(buf, binary.BigEndian, msg.Port)
|
||||
default:
|
||||
err = fmt.Errorf("unknown message type: %v", msg.Type)
|
||||
}
|
||||
}
|
||||
data = make([]byte, 4+buf.Len())
|
||||
binary.BigEndian.PutUint32(data, uint32(buf.Len()))
|
||||
if buf.Len() != copy(data[4:], buf.Bytes()) {
|
||||
panic("bad copy")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalBitfield(bf []bool) (b []byte) {
|
||||
b = make([]byte, (len(bf)+7)/8)
|
||||
for i, have := range bf {
|
||||
if !have {
|
||||
continue
|
||||
}
|
||||
c := b[i/8]
|
||||
c |= 1 << uint(7-i%8)
|
||||
b[i/8] = c
|
||||
}
|
||||
return
|
||||
}
|
26
vendor/github.com/anacrolix/torrent/peer_protocol/pex.go
generated
vendored
Normal file
26
vendor/github.com/anacrolix/torrent/peer_protocol/pex.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package peer_protocol
|
||||
|
||||
import "github.com/anacrolix/dht/krpc"
|
||||
|
||||
type PexMsg struct {
|
||||
Added krpc.CompactIPv4NodeAddrs `bencode:"added"`
|
||||
AddedFlags []PexPeerFlags `bencode:"added.f"`
|
||||
Added6 krpc.CompactIPv6NodeAddrs `bencode:"added6"`
|
||||
Added6Flags []PexPeerFlags `bencode:"added6.f"`
|
||||
Dropped krpc.CompactIPv4NodeAddrs `bencode:"dropped"`
|
||||
Dropped6 krpc.CompactIPv6NodeAddrs `bencode:"dropped6"`
|
||||
}
|
||||
|
||||
type PexPeerFlags byte
|
||||
|
||||
func (me PexPeerFlags) Get(f PexPeerFlags) bool {
|
||||
return me&f == f
|
||||
}
|
||||
|
||||
const (
|
||||
PexPrefersEncryption = 0x01
|
||||
PexSeedUploadOnly = 0x02
|
||||
PexSupportsUtp = 0x04
|
||||
PexHolepunchSupport = 0x08
|
||||
PexOutgoingConn = 0x10
|
||||
)
|
45
vendor/github.com/anacrolix/torrent/peer_protocol/protocol.go
generated
vendored
Normal file
45
vendor/github.com/anacrolix/torrent/peer_protocol/protocol.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package peer_protocol
|
||||
|
||||
const (
|
||||
Protocol = "\x13BitTorrent protocol"
|
||||
)
|
||||
|
||||
type MessageType byte
|
||||
|
||||
//go:generate stringer -type=MessageType
|
||||
|
||||
func (mt MessageType) FastExtension() bool {
|
||||
return mt >= Suggest && mt <= AllowedFast
|
||||
}
|
||||
|
||||
const (
|
||||
// BEP 3
|
||||
Choke MessageType = 0
|
||||
Unchoke MessageType = 1
|
||||
Interested MessageType = 2
|
||||
NotInterested MessageType = 3
|
||||
Have MessageType = 4
|
||||
Bitfield MessageType = 5
|
||||
Request MessageType = 6
|
||||
Piece MessageType = 7
|
||||
Cancel MessageType = 8
|
||||
Port MessageType = 9
|
||||
|
||||
// BEP 6 - Fast extension
|
||||
Suggest MessageType = 0x0d // 13
|
||||
HaveAll MessageType = 0x0e // 14
|
||||
HaveNone MessageType = 0x0f // 15
|
||||
Reject MessageType = 0x10 // 16
|
||||
AllowedFast MessageType = 0x11 // 17
|
||||
|
||||
// BEP 10
|
||||
Extended MessageType = 20
|
||||
)
|
||||
|
||||
const (
|
||||
HandshakeExtendedID = 0
|
||||
|
||||
RequestMetadataExtensionMsgType = 0
|
||||
DataMetadataExtensionMsgType = 1
|
||||
RejectMetadataExtensionMsgType = 2
|
||||
)
|
11
vendor/github.com/anacrolix/torrent/peer_protocol/reqspec.go
generated
vendored
Normal file
11
vendor/github.com/anacrolix/torrent/peer_protocol/reqspec.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package peer_protocol
|
||||
|
||||
import "fmt"
|
||||
|
||||
type RequestSpec struct {
|
||||
Index, Begin, Length Integer
|
||||
}
|
||||
|
||||
func (me RequestSpec) String() string {
|
||||
return fmt.Sprintf("{%d %d %d}", me.Index, me.Begin, me.Length)
|
||||
}
|
Reference in New Issue
Block a user