mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-06 08:30:29 +00:00
Bitfield msg decode
This commit is contained in:
1 parent
dbc58a209d
commit
b7347459eb
7 files changed
+181
-40
No files matched your search
+15
-1
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user