aboutsummaryrefslogtreecommitdiff
path: root/tracker/peer.go
blob: ab6753b425efefcce6f2b3d86bbfe5daed50f070 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package tracker

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"net/url"

	"dim13.org/btget/bencode"
)

type Peer struct {
	IP   string `bencode:"ip"`
	ID   []byte `bencode:"peer id"` // not present in compact mode
	Port int    `bencode:"port"`
}

func (p Peer) String() string {
	return fmt.Sprintf("%v:%v %v", p.IP, p.Port, url.QueryEscape(string(p.ID)))
}

func (p Peer) Addr() string {
	return fmt.Sprintf("%v:%v", p.IP, p.Port)
}

type Peers []Peer

func (p *Peers) UnmarshalBencode(b []byte) (int, error) {
	switch b[0] {
	case 'l': // XXX bencode.tokenList
		var tmp []Peer
		n, err := bencode.Unmarshal(b, &tmp)
		*p = Peers(tmp)
		return n, err
	default:
		var tmp []byte
		n, err := bencode.Unmarshal(b, &tmp)
		addr, err := peerAddr(tmp)
		*p = make(Peers, len(addr))
		for i, v := range addr {
			(*p)[i] = Peer{
				IP:   v.IP.String(),
				Port: int(v.Port),
			}
		}
		return n, err
	}
}

func peerAddr(b []byte) ([]*net.TCPAddr, error) {
	n := len(b) / 6
	a := make([]*net.TCPAddr, n)

	var port uint16
	for i := 0; i < n; i++ {
		off := i * 6
		buf := bytes.NewReader(b[off+4 : off+6])
		err := binary.Read(buf, binary.BigEndian, &port)
		if err != nil {
			return nil, err
		}
		a[i] = &net.TCPAddr{
			IP:   net.IP(b[off : off+4]),
			Port: int(port),
		}
	}
	return a, nil
}