This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

View 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
}
}())
}

View 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
}

View 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"
)

View 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
}

View 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)
}

View 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) + ")"
}
}

View 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
}

View 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
)

View 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
)

View 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)
}