2020-01-02 13:48:21 -05:00
|
|
|
package client
|
2019-12-26 17:04:32 -05:00
|
|
|
|
|
|
|
|
import (
|
2020-01-02 12:26:54 -05:00
|
|
|
"bytes"
|
2019-12-26 17:04:32 -05:00
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"time"
|
|
|
|
|
|
2019-12-29 20:24:06 -05:00
|
|
|
"github.com/veggiedefender/torrent-client/bitfield"
|
2020-01-02 10:39:35 -05:00
|
|
|
"github.com/veggiedefender/torrent-client/peers"
|
2019-12-29 20:24:06 -05:00
|
|
|
|
2019-12-26 17:04:32 -05:00
|
|
|
"github.com/veggiedefender/torrent-client/message"
|
|
|
|
|
|
|
|
|
|
"github.com/veggiedefender/torrent-client/handshake"
|
|
|
|
|
)
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// A Client is a TCP connection with a peer
|
|
|
|
|
type Client struct {
|
|
|
|
|
Conn net.Conn
|
|
|
|
|
Choked bool
|
|
|
|
|
Bitfield bitfield.Bitfield
|
2020-01-02 10:39:35 -05:00
|
|
|
peer peers.Peer
|
2019-12-26 21:53:11 -05:00
|
|
|
infoHash [20]byte
|
|
|
|
|
peerID [20]byte
|
2019-12-26 17:04:32 -05:00
|
|
|
}
|
|
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
func completeHandshake(conn net.Conn, infohash, peerID [20]byte) (*handshake.Handshake, error) {
|
2019-12-29 21:43:58 -05:00
|
|
|
conn.SetDeadline(time.Now().Add(3 * time.Second))
|
2019-12-26 17:04:32 -05:00
|
|
|
defer conn.SetDeadline(time.Time{}) // Disable the deadline
|
|
|
|
|
|
|
|
|
|
req := handshake.New(infohash, peerID)
|
|
|
|
|
_, err := conn.Write(req.Serialize())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
res, err := handshake.Read(conn)
|
2019-12-26 17:04:32 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-01-02 12:26:54 -05:00
|
|
|
if !bytes.Equal(res.InfoHash[:], infohash[:]) {
|
|
|
|
|
return nil, fmt.Errorf("Expected infohash %x but got %x", res.InfoHash, infohash)
|
|
|
|
|
}
|
2019-12-26 17:04:32 -05:00
|
|
|
return res, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
func recvBitfield(conn net.Conn) (bitfield.Bitfield, error) {
|
2019-12-29 21:43:58 -05:00
|
|
|
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
2019-12-26 17:04:32 -05:00
|
|
|
defer conn.SetDeadline(time.Time{}) // Disable the deadline
|
|
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
msg, err := message.Read(conn)
|
2019-12-26 17:04:32 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-09-28 14:11:01 +03:00
|
|
|
if msg == nil {
|
|
|
|
|
err := fmt.Errorf("Expected bitfield but got %s", msg)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-26 17:04:32 -05:00
|
|
|
if msg.ID != message.MsgBitfield {
|
|
|
|
|
err := fmt.Errorf("Expected bitfield but got ID %d", msg.ID)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return msg.Payload, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// New connects with a peer, completes a handshake, and receives a handshake
|
|
|
|
|
// returns an err if any of those fail.
|
|
|
|
|
func New(peer peers.Peer, peerID, infoHash [20]byte) (*Client, error) {
|
2020-01-03 13:04:16 -05:00
|
|
|
conn, err := net.DialTimeout("tcp", peer.String(), 3*time.Second)
|
2019-12-26 17:04:32 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-26 21:53:11 -05:00
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
_, err = completeHandshake(conn, infoHash, peerID)
|
2019-12-26 17:04:32 -05:00
|
|
|
if err != nil {
|
2019-12-29 14:02:50 -05:00
|
|
|
conn.Close()
|
2019-12-26 17:04:32 -05:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-26 21:53:11 -05:00
|
|
|
|
2020-01-02 19:36:25 -05:00
|
|
|
bf, err := recvBitfield(conn)
|
2019-12-26 17:04:32 -05:00
|
|
|
if err != nil {
|
2019-12-29 14:02:50 -05:00
|
|
|
conn.Close()
|
2019-12-26 17:04:32 -05:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-26 21:53:11 -05:00
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
return &Client{
|
|
|
|
|
Conn: conn,
|
|
|
|
|
Choked: true,
|
|
|
|
|
Bitfield: bf,
|
2019-12-26 21:53:11 -05:00
|
|
|
peer: peer,
|
|
|
|
|
infoHash: infoHash,
|
|
|
|
|
peerID: peerID,
|
2019-12-26 17:04:32 -05:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// Read reads and consumes a message from the connection
|
|
|
|
|
func (c *Client) Read() (*message.Message, error) {
|
2020-01-02 19:36:25 -05:00
|
|
|
msg, err := message.Read(c.Conn)
|
2019-12-26 21:53:11 -05:00
|
|
|
return msg, err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// SendRequest sends a Request message to the peer
|
|
|
|
|
func (c *Client) SendRequest(index, begin, length int) error {
|
2019-12-26 21:53:11 -05:00
|
|
|
req := message.FormatRequest(index, begin, length)
|
2020-01-02 13:48:21 -05:00
|
|
|
_, err := c.Conn.Write(req.Serialize())
|
2019-12-26 21:53:11 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// SendInterested sends an Interested message to the peer
|
|
|
|
|
func (c *Client) SendInterested() error {
|
2019-12-26 21:53:11 -05:00
|
|
|
msg := message.Message{ID: message.MsgInterested}
|
2020-01-02 13:48:21 -05:00
|
|
|
_, err := c.Conn.Write(msg.Serialize())
|
2019-12-26 21:53:11 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// SendNotInterested sends a NotInterested message to the peer
|
|
|
|
|
func (c *Client) SendNotInterested() error {
|
2019-12-26 21:53:11 -05:00
|
|
|
msg := message.Message{ID: message.MsgNotInterested}
|
2020-01-02 13:48:21 -05:00
|
|
|
_, err := c.Conn.Write(msg.Serialize())
|
2019-12-26 21:53:11 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// SendUnchoke sends an Unchoke message to the peer
|
|
|
|
|
func (c *Client) SendUnchoke() error {
|
2019-12-26 21:53:11 -05:00
|
|
|
msg := message.Message{ID: message.MsgUnchoke}
|
2020-01-02 13:48:21 -05:00
|
|
|
_, err := c.Conn.Write(msg.Serialize())
|
2019-12-26 21:53:11 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-02 13:48:21 -05:00
|
|
|
// SendHave sends a Have message to the peer
|
|
|
|
|
func (c *Client) SendHave(index int) error {
|
2019-12-27 14:33:03 -05:00
|
|
|
msg := message.FormatHave(index)
|
2020-01-02 13:48:21 -05:00
|
|
|
_, err := c.Conn.Write(msg.Serialize())
|
2019-12-26 21:53:11 -05:00
|
|
|
return err
|
|
|
|
|
}
|