Bitfield msg decode

This commit is contained in:
liwei committed 2019-06-26 11:23:39 +08:00
1 parent dbc58a209d
commit b7347459eb
7 files changed
+181 -40

No files matched your search

+15 -1
View File
@@ -6,5 +6,19 @@ import (
func TestClient_AddTorrent(t *testing.T) {
client := NewClient()
client.AddTorrent("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
if err != nil {
panic(err)
}
torrent := &Torrent{
client: client,
MetaInfo: metaInfo,
}
torrent.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce"
torrent.MetaInfo.AnnounceList = [][]string{}
err = torrent.Tracker()
if err != nil {
panic(err)
return
}
}
+1 -1
View File
@@ -60,7 +60,7 @@ func ParseFromFile(path string) (*MetaInfo, error) {
var metaInfo MetaInfo
json.Unmarshal(bytes, &metaInfo)
if metaInfo.Info != nil {
// Encode info hash
// encode info hash
info := btDecode["info"].(map[string]interface{})
infoBtEncode := bencode.Encode(info)
metaInfo.InfoHash = sha1.Sum(infoBtEncode[:])
+39 -2
View File
@@ -34,6 +34,15 @@ type Peer struct {
Torrent *Torrent
conn net.Conn
// this client is choking the peer
AmChoking bool
// this client is interested in the peer
AmInterested bool
// peer is choking this client
PeerChoking bool
// peer is interested in this client
PeerInterested bool
}
func (peer *Peer) Address() string {
@@ -56,7 +65,19 @@ type Message struct {
Payload []byte
}
func NewHandshake(reserved [8]byte, infoHash [20]byte, peerID [20]byte) *Handshake {
type MessageBitfield []bool
func decodeBitfield(payload []byte) MessageBitfield {
bits := make([]bool, len(payload)*8)
for i, b := range payload {
for j := 0; j < 8; j++ {
bits[i*8+j] = b&(1<<uint(7-j)) > 0
}
}
return bits
}
func newHandshake(reserved [8]byte, infoHash [20]byte, peerID [20]byte) *Handshake {
var arr [ProtocolIdentifierLength]byte
copy(arr[:], ProtocolIdentifier)
return &Handshake{
@@ -112,6 +133,11 @@ func (peer *Peer) DoDownload() error {
if err != nil {
return err
}
// handshake success,init status
peer.AmChoking = true
peer.AmInterested = false
peer.PeerChoking = true
peer.PeerInterested = false
scanner := bufio.NewScanner(conn)
scanner.Split(splitMessage)
@@ -124,16 +150,22 @@ func (peer *Peer) DoDownload() error {
} else {
switch MessageType(buf[4]) {
case Choke:
peer.PeerChoking = true
break
case Unchoke:
peer.PeerChoking = false
break
case Interested:
peer.PeerInterested = true
break
case NotInterested:
peer.PeerInterested = false
break
case Have:
break
case Bitfield:
bitfield := decodeBitfield(message.Payload)
fmt.Println(bitfield)
break
case Request:
break
@@ -229,7 +261,7 @@ func (peer *Peer) doHandshake(conn net.Conn) error {
/* reserved[5] = 0x10
reserved[6] = 0x0
reserved[7] = 0x5*/
handshakeReq := NewHandshake(reserved, metaInfo.InfoHash, peerID)
handshakeReq := newHandshake(reserved, metaInfo.InfoHash, peerID)
buf, err := handshakeReq.encode()
if err != nil {
return err
@@ -252,6 +284,11 @@ func (peer *Peer) doHandshake(conn net.Conn) error {
return nil
}
// Message protocol
// length prefix| message ID | payload
// 4-byte | 1-byte | remaining bytes
// 100 | 5 | 99-bytes
// https://wiki.theory.org/index.php/BitTorrentSpecification#Messages
func splitMessage(data []byte, atEOF bool) (advance int, token []byte, err error) {
if !atEOF && len(data) > 4 {
length := int(binary.BigEndian.Uint32(data))
+41
View File
@@ -0,0 +1,41 @@
package bt
import (
"fmt"
"reflect"
"testing"
)
func Test_decodeBitfield(t *testing.T) {
fmt.Printf("%08b", 0xFF)
type args struct {
payload []byte
}
tests := []struct {
name string
args args
want MessageBitfield
}{
{
"case-11111111",
args{
[]byte{0xFF},
},
[]bool{true, true, true, true, true, true, true, true},
},
{
"case-10101010",
args{
[]byte{0xAA},
},
[]bool{true, false, true, false, true, false, true, false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := decodeBitfield(tt.args.payload); !reflect.DeepEqual(got, tt.want) {
t.Errorf("decodeBitfield() = %v, want %v", got, tt.want)
}
})
}
}
+41 -5
View File
@@ -2,6 +2,7 @@ package bt
import (
"fmt"
"io"
)
type Torrent struct {
@@ -11,14 +12,49 @@ type Torrent struct {
Peers []Peer
}
// Handshake of Peer wire protocol
// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake
func (torrent *Torrent) doHandshake(peer *Peer) error {
conn, err := dialMse(peer.Address(), peer.Torrent.MetaInfo.InfoHash)
if err != nil {
return err
}
defer conn.Close()
metaInfo := peer.Torrent.MetaInfo
peerID := peer.Torrent.client.PeerID
reserved := [8]byte{}
/* reserved[5] = 0x10
reserved[6] = 0x0
reserved[7] = 0x5*/
handshakeReq := newHandshake(reserved, metaInfo.InfoHash, peerID)
buf, err := handshakeReq.encode()
if err != nil {
return err
}
conn.Write(buf)
var read [68]byte
_, err = io.ReadFull(conn, read[:])
if err != nil {
return err
}
handshakeRes := new(Handshake)
err = handshakeRes.decode(read[:])
if err != nil {
return err
}
if handshakeRes.InfoHash != handshakeReq.InfoHash {
return err
}
return nil
}
func (torrent *Torrent) Download() {
t := NewTracker(torrent)
peers, err := t.Tracker()
err := torrent.Tracker()
if err != nil {
fmt.Println(err)
return
}
peers[0].Torrent = torrent
peers[0].DoDownload()
}
+20 -31
View File
@@ -25,14 +25,6 @@ const (
udpActionError = 3
)
type Tracker struct {
torrent *Torrent
}
func NewTracker(torrent *Torrent) *Tracker {
return &Tracker{torrent: torrent}
}
/*
Offset Size Name Value
0 64-bit integer protocol_id 0x41727101980 // magic constant
@@ -55,7 +47,7 @@ func newUdpConnectRequest() *udpConnectRequest {
}
}
func (req *udpConnectRequest) Encode() []byte {
func (req *udpConnectRequest) encode() []byte {
buf := make([]byte, 16)
binary.BigEndian.PutUint64(buf[:8], req.protocolId)
binary.BigEndian.PutUint32(buf[8:12], req.action)
@@ -186,9 +178,9 @@ func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse {
return response
}
func (tracker *Tracker) connect(conn *net.UDPConn, timeout int64) (response *udpConnectResponse, err error) {
func (torrent *Torrent) connect(conn *net.UDPConn, timeout int64) (response *udpConnectResponse, err error) {
request := newUdpConnectRequest()
buf := request.Encode()
buf := request.encode()
conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout)))
conn.Write(buf)
len, err := conn.Read(buf)
@@ -203,9 +195,7 @@ func (tracker *Tracker) connect(conn *net.UDPConn, timeout int64) (response *udp
return
}
func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) {
torrent := tracker.torrent
func (torrent *Torrent) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) {
request := newUdpAnnounceRequest(connectionId)
request.infoHash = torrent.MetaInfo.InfoHash
request.peerID = torrent.client.PeerID
@@ -215,7 +205,7 @@ func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId
request.event = 0
request.ip = 0
request.key = 0
request.numWant = -1
request.numWant = 50
request.port = 6882
encode := request.encode()
conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout)))
@@ -230,8 +220,9 @@ func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId
return
}
func (tracker *Tracker) Tracker() (peers []Peer, err error) {
metaInfo := tracker.torrent.MetaInfo
// 通过tracker服务器获取peer信息
func (torrent *Torrent) Tracker() (err error) {
metaInfo := torrent.MetaInfo
checkAnnounceList := func(announceList [][]string) bool {
if len(announceList) > 0 {
@@ -244,11 +235,12 @@ func (tracker *Tracker) Tracker() (peers []Peer, err error) {
return false
}
// 只要获取到了peers就直接返回
if checkAnnounceList(metaInfo.AnnounceList) {
for _, announceArr := range metaInfo.AnnounceList {
if len(announceArr) > 0 {
for _, announce := range announceArr {
peers, err = tracker.doTracker(announce)
torrent.Peers, err = torrent.doTracker(announce)
if err == nil {
return
}
@@ -256,24 +248,21 @@ func (tracker *Tracker) Tracker() (peers []Peer, err error) {
}
}
} else {
peers, err = tracker.doTracker(metaInfo.Announce)
if err == nil {
return
}
torrent.Peers, err = torrent.doTracker(metaInfo.Announce)
}
return
}
func (tracker *Tracker) doTracker(announce string) (peers []Peer, err error) {
func (torrent *Torrent) doTracker(announce string) (peers []Peer, err error) {
if announce != "" {
url, _ := url.Parse(announce)
switch url.Scheme {
case "http", "https":
// TODO UDP test
break
return tracker.httpTracker(url)
return torrent.httpTracker(url)
case "udp", "udp4", "udp6":
return tracker.udpTracker(url)
return torrent.udpTracker(url)
default:
return nil, errors.New("unsupported protocol")
}
@@ -282,9 +271,9 @@ func (tracker *Tracker) doTracker(announce string) (peers []Peer, err error) {
}
// http://bittorrent.org/beps/bep_0003.html#trackers
func (tracker *Tracker) httpTracker(url *url.URL) (peers []Peer, err error) {
metaInfo := tracker.torrent.MetaInfo
peerID := tracker.torrent.client.PeerID
func (torrent *Torrent) httpTracker(url *url.URL) (peers []Peer, err error) {
metaInfo := torrent.MetaInfo
peerID := torrent.client.PeerID
query := url.Query()
query.Add("info_hash", string(metaInfo.InfoHash[:]))
@@ -319,7 +308,7 @@ func (tracker *Tracker) httpTracker(url *url.URL) (peers []Peer, err error) {
}
// http://bittorrent.org/beps/bep_0015.html
func (tracker *Tracker) udpTracker(url *url.URL) (peers []Peer, err error) {
func (torrent *Torrent) udpTracker(url *url.URL) (peers []Peer, err error) {
conn, err := dial(url)
if err != nil {
return
@@ -336,7 +325,7 @@ func (tracker *Tracker) udpTracker(url *url.URL) (peers []Peer, err error) {
timeout := int64(udpConnectTimeout * math.Pow(2, float64(n)))
switch state {
case 0:
connectResponse, err = tracker.connect(conn, timeout)
connectResponse, err = torrent.connect(conn, timeout)
if err != nil {
if oe, ok := err.(*net.OpError); ok && oe.Timeout() {
break
@@ -348,7 +337,7 @@ func (tracker *Tracker) udpTracker(url *url.URL) (peers []Peer, err error) {
state = 1
break
case 1:
announceResponse, err = tracker.announce(conn, timeout, connectResponse.connectionId)
announceResponse, err = torrent.announce(conn, timeout, connectResponse.connectionId)
if err != nil {
if oe, ok := err.(*net.OpError); ok && oe.Timeout() {
break
+24
View File
@@ -0,0 +1,24 @@
package bt
import (
"testing"
)
func TestTracker_Tracker(t *testing.T) {
client := NewClient()
metaInfo, err := ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
if err != nil {
panic(err)
}
torrent := &Torrent{
client: client,
MetaInfo: metaInfo,
}
torrent.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce"
torrent.MetaInfo.AnnounceList = [][]string{}
err = torrent.Tracker()
if err != nil {
panic(err)
return
}
}