15 "github.com/anacrolix/dht/krpc"
16 "github.com/anacrolix/missinggo"
17 "github.com/anacrolix/missinggo/pproffd"
23 ActionConnect Action = iota
28 connectRequestConnectionId = 0x41727101980
31 optionTypeEndOfOptions = 0
36 type ConnectionRequest struct {
42 type ConnectionResponse struct {
46 type ResponseHeader struct {
51 type RequestHeader struct {
57 type AnnounceResponseHeader struct {
63 func newTransactionId() int32 {
64 return int32(rand.Uint32())
67 func timeout(contiguousTimeouts int) (d time.Duration) {
68 if contiguousTimeouts > 8 {
69 contiguousTimeouts = 8
72 for ; contiguousTimeouts > 0; contiguousTimeouts-- {
78 type udpAnnounce struct {
79 contiguousTimeouts int
80 connectionIdReceived time.Time
87 func (c *udpAnnounce) Close() error {
89 return c.socket.Close()
94 func (c *udpAnnounce) ipv6() bool {
95 if c.a.UdpNetwork == "udp6" {
98 rip := missinggo.AddrIP(c.socket.RemoteAddr())
99 return rip.To16() != nil && rip.To4() == nil
102 func (c *udpAnnounce) Do(req *AnnounceRequest) (res AnnounceResponse, err error) {
107 reqURI := c.url.RequestURI()
108 // Clearly this limits the request URI to 255 bytes. BEP 41 supports
109 // longer but I'm not fussed.
110 options := append([]byte{optionTypeURLData, byte(len(reqURI))}, []byte(reqURI)...)
111 b, err := c.request(ActionAnnounce, req, options)
115 var h AnnounceResponseHeader
116 err = readBody(b, &h)
119 err = io.ErrUnexpectedEOF
121 err = fmt.Errorf("error parsing announce response: %s", err)
124 res.Interval = h.Interval
125 res.Leechers = h.Leechers
126 res.Seeders = h.Seeders
127 nas := func() interface {
128 encoding.BinaryUnmarshaler
129 NodeAddrs() []krpc.NodeAddr
132 return &krpc.CompactIPv6NodeAddrs{}
134 return &krpc.CompactIPv4NodeAddrs{}
137 err = nas.UnmarshalBinary(b.Bytes())
141 for _, cp := range nas.NodeAddrs() {
142 res.Peers = append(res.Peers, Peer{}.FromNodeAddr(cp))
147 // body is the binary serializable request body. trailer is optional data
148 // following it, such as for BEP 41.
149 func (c *udpAnnounce) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
151 err = binary.Write(&buf, binary.BigEndian, h)
156 err = binary.Write(&buf, binary.BigEndian, body)
161 _, err = buf.Write(trailer)
165 n, err := c.socket.Write(buf.Bytes())
170 panic("write should send all or error")
175 func read(r io.Reader, data interface{}) error {
176 return binary.Read(r, binary.BigEndian, data)
179 func write(w io.Writer, data interface{}) error {
180 return binary.Write(w, binary.BigEndian, data)
183 // args is the binary serializable request body. trailer is optional data
184 // following it, such as for BEP 41.
185 func (c *udpAnnounce) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
186 tid := newTransactionId()
187 err = c.write(&RequestHeader{
188 ConnectionId: c.connectionId,
195 c.socket.SetReadDeadline(time.Now().Add(timeout(c.contiguousTimeouts)))
196 b := make([]byte, 0x800) // 2KiB
199 n, err = c.socket.Read(b)
200 if opE, ok := err.(*net.OpError); ok {
202 c.contiguousTimeouts++
209 buf := bytes.NewBuffer(b[:n])
211 err = binary.Read(buf, binary.BigEndian, &h)
213 case io.ErrUnexpectedEOF:
219 if h.TransactionId != tid {
222 c.contiguousTimeouts = 0
223 if h.Action == ActionError {
224 err = errors.New(buf.String())
231 func readBody(r io.Reader, data ...interface{}) (err error) {
232 for _, datum := range data {
233 err = binary.Read(r, binary.BigEndian, datum)
241 func (c *udpAnnounce) connected() bool {
242 return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
245 func (c *udpAnnounce) dialNetwork() string {
246 if c.a.UdpNetwork != "" {
247 return c.a.UdpNetwork
252 func (c *udpAnnounce) connect() (err error) {
256 c.connectionId = connectRequestConnectionId
258 hmp := missinggo.SplitHostMaybePort(c.url.Host)
263 c.socket, err = net.Dial(c.dialNetwork(), hmp.String())
267 c.socket = pproffd.WrapNetConn(c.socket)
269 b, err := c.request(ActionConnect, nil, nil)
273 var res ConnectionResponse
274 err = readBody(b, &res)
278 c.connectionId = res.ConnectionId
279 c.connectionIdReceived = time.Now()
283 // TODO: Split on IPv6, as BEP 15 says response peer decoding depends on
285 func announceUDP(opt Announce, _url *url.URL) (AnnounceResponse, error) {
291 return ua.Do(&opt.Request)