2019-12-22 17:43:39 -05:00
|
|
|
package p2p
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net"
|
|
|
|
|
"strconv"
|
2019-12-24 13:23:46 -05:00
|
|
|
"time"
|
2019-12-22 23:51:31 -05:00
|
|
|
|
2019-12-24 10:39:22 -05:00
|
|
|
"github.com/veggiedefender/torrent-client/handshake"
|
2019-12-22 17:43:39 -05:00
|
|
|
)
|
|
|
|
|
|
2019-12-22 22:25:57 -05:00
|
|
|
// Peer encodes connection information for a peer
|
2019-12-22 17:43:39 -05:00
|
|
|
type Peer struct {
|
|
|
|
|
IP net.IP
|
|
|
|
|
Port uint16
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-24 13:23:46 -05:00
|
|
|
// Download holds data required to download a torrent from a list of peers
|
|
|
|
|
type Download struct {
|
2019-12-22 23:51:31 -05:00
|
|
|
Peers []Peer
|
|
|
|
|
PeerID [20]byte
|
|
|
|
|
InfoHash [20]byte
|
|
|
|
|
PieceHashes [][20]byte
|
|
|
|
|
Length int
|
2019-12-22 22:25:57 -05:00
|
|
|
}
|
2019-12-22 17:43:39 -05:00
|
|
|
|
2019-12-24 13:23:46 -05:00
|
|
|
type peerState struct {
|
|
|
|
|
peer *Peer
|
|
|
|
|
conn net.Conn
|
|
|
|
|
}
|
2019-12-22 23:51:31 -05:00
|
|
|
|
2019-12-24 13:23:46 -05:00
|
|
|
type swarm struct {
|
|
|
|
|
peerStates []*peerState
|
2019-12-22 22:25:57 -05:00
|
|
|
}
|
2019-12-22 17:43:39 -05:00
|
|
|
|
2019-12-22 23:51:31 -05:00
|
|
|
func (p *Peer) connect(peerID [20]byte, infoHash [20]byte) (net.Conn, error) {
|
2019-12-22 22:25:57 -05:00
|
|
|
hostPort := net.JoinHostPort(p.IP.String(), strconv.Itoa(int(p.Port)))
|
2019-12-24 13:23:46 -05:00
|
|
|
conn, err := net.DialTimeout("tcp", hostPort, 3*time.Second)
|
2019-12-22 17:43:39 -05:00
|
|
|
if err != nil {
|
2019-12-22 22:25:57 -05:00
|
|
|
return nil, err
|
2019-12-22 17:43:39 -05:00
|
|
|
}
|
2019-12-22 22:25:57 -05:00
|
|
|
return conn, nil
|
2019-12-22 17:43:39 -05:00
|
|
|
}
|
2019-12-22 23:51:31 -05:00
|
|
|
|
2019-12-24 13:23:46 -05:00
|
|
|
func (d *Download) handshake(conn net.Conn) (*handshake.Handshake, error) {
|
|
|
|
|
conn.SetDeadline(time.Now().Local().Add(3 * time.Second))
|
2019-12-24 11:05:22 -05:00
|
|
|
req := handshake.New(d.InfoHash, d.PeerID)
|
2019-12-22 23:51:31 -05:00
|
|
|
_, err := conn.Write(req.Serialize())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-24 10:39:22 -05:00
|
|
|
res, err := handshake.Read(conn)
|
2019-12-22 23:51:31 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-24 13:23:46 -05:00
|
|
|
conn.SetDeadline(time.Time{}) // Disable the deadline
|
2019-12-22 23:51:31 -05:00
|
|
|
return res, nil
|
|
|
|
|
}
|
2019-12-24 13:23:46 -05:00
|
|
|
|
|
|
|
|
func (d *Download) initPeer(p *Peer, c chan *peerState) {
|
|
|
|
|
conn, err := p.connect(d.PeerID, d.InfoHash)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c <- nil
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, err = d.handshake(conn)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c <- nil
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
c <- &peerState{p, conn}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Download) startSwarm() *swarm {
|
|
|
|
|
c := make(chan *peerState)
|
|
|
|
|
for i := range d.Peers {
|
|
|
|
|
go d.initPeer(&d.Peers[i], c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
peerStates := make([]*peerState, 0)
|
|
|
|
|
for range d.Peers {
|
|
|
|
|
ps := <-c
|
|
|
|
|
if ps != nil {
|
|
|
|
|
peerStates = append(peerStates, ps)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &swarm{peerStates}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Download downloads a torrent
|
|
|
|
|
func (d *Download) Download() error {
|
|
|
|
|
d.startSwarm()
|
|
|
|
|
return nil
|
|
|
|
|
}
|