-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathkrpc.go
205 lines (180 loc) · 6.78 KB
/
krpc.go
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package dht
import (
"encoding/hex"
"net"
// "log"
)
// Ping is the most basic query. "q" = "ping" A ping query has a single argument,
// "id" the value is a 20-byte string containing the senders node ID in network byte
// order. The appropriate response to a ping has a single key "id" containing the
// node ID of the responding node.
// arguments: {"id" : "<querying nodes id>"}
// http://www.bittorrent.org/beps/bep_0005.html#ping
func (node *Node) Ping(addr *net.UDPAddr) error {
req := KRPCQuery{
T: node.tokenManager.GenToken(),
Q: PingType,
NID: node.ID,
}
data, err := req.Encode()
if err != nil {
return err
}
// log.Printf("send ping query to %s:%d\n", addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// response: {"id" : "<queried nodes id>"}
func (node *Node) onPingQuery(query *KRPCQuery, addr *net.UDPAddr) error {
response := KRPCResponse{
T: node.tokenManager.GenToken(),
Q: PingType,
QueriedID: node.ID,
}
data, err := response.Encode()
if err != nil {
return err
}
// log.Printf("send %v response to %s:%d\n", response, addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// FindNode is used to find the contact information for a node given its ID.
// "q" == "find_node" A find_node query has two arguments, "id" containing
// the node ID of the querying node, and "target" containing the ID of the
// node sought by the queryer. When a node receives a find_node query, it
// should respond with a key "nodes" and value of a string containing the
// compact node info for the target node or the K (8) closest good nodes in
// its own routing table.
// arguments: {"id" : "<querying nodes id>", "target" : "<id of target node>"}
// http://www.bittorrent.org/beps/bep_0005.html#find-node
func (node *Node) FindNode(addr *net.UDPAddr, nid NodeID) error {
req := KRPCQuery{
T: node.tokenManager.GenToken(),
Q: FindNodeType,
NID: node.ID,
TargetNID: nid,
}
data, err := req.Encode()
if err != nil {
log.Println(err)
return err
}
// log.Printf("send find_node query to %s:%d\n", addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// response: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}
func (node *Node) onFindNodeQuery(query *KRPCQuery, addr *net.UDPAddr) error {
// var nodes = make([]*NodeInfo, 0, 8)
// for i := 0; i < 8; i++ {
// nodes = append(nodes, &NodeInfo{
// ID: GetNeighborNID(node.ID, query.TargetNID),
// UDPAddr: node.UDPAddr,
// })
// }
response := KRPCResponse{
T: query.T,
Q: FindNodeType,
QueriedID: node.ID,
Nodes: make([]*NodeInfo, 0),
}
data, err := response.Encode()
if err != nil {
return err
}
// log.Printf("send %v response to %s:%d\n", response, addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// GetPeers gets peers associated with a torrent infohash. "q" = "get_peers" A get_peers
// query has two arguments, "id" containing the node ID of the querying node,
// and "info_hash" containing the infohash of the torrent. If the queried node
// has peers for the infohash, they are returned in a key "values" as a list
// of strings. Each string containing "compact" format peer information for a
// single peer. If the queried node has no peers for the infohash, a key "nodes"
// is returned containing the K nodes in the queried nodes routing table closest
// to the infohash supplied in the query. In either case a "token" key is also
// included in the return value. The token value is a required argument for a
// future announce_peer query. The token value should be a short binary string.
// arguments: {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>"}
// http://www.bittorrent.org/beps/bep_0005.html#get-peers
func (node *Node) GetPeers(addr *net.UDPAddr, infoHash []byte) error {
rid := GenerateNodeID()
query := KRPCQuery{
T: node.tokenManager.GenToken(),
NID: node.ID,
InfoHash: rid[:],
}
data, err := query.Encode()
if err != nil {
return err
}
// log.Printf("send find_node query to %s:%d\n", addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// response: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "values" : ["<peer 1 info string>", "<peer 2 info string>"]}
func (node *Node) onGetPeersQuery(query *KRPCQuery, addr *net.UDPAddr) error {
if len(query.InfoHash) == 0 {
query.InfoHash = generateBytes()
}
response := KRPCResponse{
T: query.T,
Q: GetPeersType,
QueriedID: GetNeighborNID(node.ID, query.InfoHash),
Token: string(query.InfoHash[:2]),
Nodes: make([]*NodeInfo, 0),
}
data, err := response.Encode()
if err != nil {
return err
}
// log.Printf("send %s response to %s:%d\n", string(data), addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}
// AnnouncePeer announces that the peer, controlling the querying node, is downloading a torrent on a port.
// announce_peer has four arguments: "id" containing the node ID of the querying node, "info_hash" containing
// the infohash of the torrent, "port" containing the port as an integer, and the "token" received in response
// to a previous get_peers query. There is an optional argument called implied_port which value is either 0 or 1.
// If it is present and non-zero, the port argument should be ignored and the source port of the UDP packet
// should be used as the peer's port instead. This is useful for peers behind a NAT that may not know their
// external port, and supporting uTP, they accept incoming.
// arguments: {"id" : "<querying nodes id>",
// "implied_port": <0 or 1>,
// "info_hash" : "<20-byte infohash of target torrent>",
// "port" : <port number>,
// "token" : "<opaque token>"}
// reference: http://www.bittorrent.org/beps/bep_0005.html#announce_peer
func (node *Node) AnnouncePeer(addr *net.UDPAddr, infoHash []byte, token string, impliedPort int8, port int) error {
req := KRPCQuery{
T: node.tokenManager.GenToken(),
Q: AnnouncePeerType,
NID: node.ID,
InfoHash: infoHash,
Token: token,
ImpliedPort: impliedPort,
Port: port,
}
data, err := req.Encode()
if err != nil {
return err
}
return node.writeToUDP(addr, data)
}
// response: {"id" : "<queried nodes id>"}
func (node *Node) onAnnouncePeer(query *KRPCQuery, addr *net.UDPAddr) error {
port := query.Port
if query.ImpliedPort == 1 {
port = addr.Port
}
node.PeerHandler(addr.IP.String(), port,
hex.EncodeToString(query.InfoHash),
hex.EncodeToString(query.NID[:]))
response := KRPCResponse{
T: query.T,
Q: AnnouncePeerType,
QueriedID: node.ID,
}
data, err := response.Encode()
if err != nil {
return err
}
// log.Printf("send %v response to %s:%d\n", response, addr.IP.String(), addr.Port)
return node.writeToUDP(addr, data)
}