]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/udp_test.go
Fix tracker test timeouts
[btrtrc.git] / tracker / udp_test.go
1 package tracker
2
3 import (
4         "bytes"
5         "context"
6         "crypto/rand"
7         "encoding/binary"
8         "errors"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "net"
13         "net/url"
14         "sync"
15         "testing"
16         "time"
17
18         "github.com/anacrolix/dht/v2/krpc"
19         _ "github.com/anacrolix/envpprof"
20         "github.com/anacrolix/torrent/tracker/udp"
21         "github.com/stretchr/testify/assert"
22         "github.com/stretchr/testify/require"
23 )
24
25 var trackers = []string{
26         "udp://tracker.coppersurfer.tk:6969",
27         "udp://tracker.leechers-paradise.org:6969",
28 }
29
30 // Ensure net.IPs are stored big-endian, to match the way they're read from
31 // the wire.
32 func TestNetIPv4Bytes(t *testing.T) {
33         ip := net.IP([]byte{127, 0, 0, 1})
34         if ip.String() != "127.0.0.1" {
35                 t.FailNow()
36         }
37         if string(ip) != "\x7f\x00\x00\x01" {
38                 t.Fatal([]byte(ip))
39         }
40 }
41
42 func TestMarshalAnnounceResponse(t *testing.T) {
43         peers := krpc.CompactIPv4NodeAddrs{
44                 {[]byte{127, 0, 0, 1}, 2},
45                 {[]byte{255, 0, 0, 3}, 4},
46         }
47         b, err := peers.MarshalBinary()
48         require.NoError(t, err)
49         require.EqualValues(t,
50                 "\x7f\x00\x00\x01\x00\x02\xff\x00\x00\x03\x00\x04",
51                 b)
52         require.EqualValues(t, 12, binary.Size(udp.AnnounceResponseHeader{}))
53 }
54
55 // Failure to write an entire packet to UDP is expected to given an error.
56 func TestLongWriteUDP(t *testing.T) {
57         t.Parallel()
58         l, err := net.ListenUDP("udp4", nil)
59         require.NoError(t, err)
60         defer l.Close()
61         c, err := net.DialUDP("udp", nil, l.LocalAddr().(*net.UDPAddr))
62         if err != nil {
63                 t.Fatal(err)
64         }
65         defer c.Close()
66         for msgLen := 1; ; msgLen *= 2 {
67                 n, err := c.Write(make([]byte, msgLen))
68                 if err != nil {
69                         require.Contains(t, err.Error(), "message too long")
70                         return
71                 }
72                 if n < msgLen {
73                         t.FailNow()
74                 }
75         }
76 }
77
78 func TestShortBinaryRead(t *testing.T) {
79         var data udp.ResponseHeader
80         err := binary.Read(bytes.NewBufferString("\x00\x00\x00\x01"), binary.BigEndian, &data)
81         if err != io.ErrUnexpectedEOF {
82                 t.FailNow()
83         }
84 }
85
86 func TestConvertInt16ToInt(t *testing.T) {
87         i := 50000
88         if int(uint16(int16(i))) != 50000 {
89                 t.FailNow()
90         }
91 }
92
93 func TestAnnounceLocalhost(t *testing.T) {
94         t.Parallel()
95         srv := server{
96                 t: map[[20]byte]torrent{
97                         {0xa3, 0x56, 0x41, 0x43, 0x74, 0x23, 0xe6, 0x26, 0xd9, 0x38, 0x25, 0x4a, 0x6b, 0x80, 0x49, 0x10, 0xa6, 0x67, 0xa, 0xc1}: {
98                                 Seeders:  1,
99                                 Leechers: 2,
100                                 Peers: krpc.CompactIPv4NodeAddrs{
101                                         {[]byte{1, 2, 3, 4}, 5},
102                                         {[]byte{6, 7, 8, 9}, 10},
103                                 },
104                         },
105                 },
106         }
107         var err error
108         srv.pc, err = net.ListenPacket("udp", ":0")
109         require.NoError(t, err)
110         defer srv.pc.Close()
111         go func() {
112                 require.NoError(t, srv.serveOne())
113         }()
114         req := AnnounceRequest{
115                 NumWant: -1,
116                 Event:   Started,
117         }
118         rand.Read(req.PeerId[:])
119         copy(req.InfoHash[:], []uint8{0xa3, 0x56, 0x41, 0x43, 0x74, 0x23, 0xe6, 0x26, 0xd9, 0x38, 0x25, 0x4a, 0x6b, 0x80, 0x49, 0x10, 0xa6, 0x67, 0xa, 0xc1})
120         go func() {
121                 require.NoError(t, srv.serveOne())
122         }()
123         ar, err := Announce{
124                 TrackerUrl: fmt.Sprintf("udp://%s/announce", srv.pc.LocalAddr().String()),
125                 Request:    req,
126         }.Do()
127         require.NoError(t, err)
128         assert.EqualValues(t, 1, ar.Seeders)
129         assert.EqualValues(t, 2, len(ar.Peers))
130 }
131
132 func TestUDPTracker(t *testing.T) {
133         t.Parallel()
134         if testing.Short() {
135                 t.SkipNow()
136         }
137         req := AnnounceRequest{
138                 NumWant: -1,
139         }
140         rand.Read(req.PeerId[:])
141         copy(req.InfoHash[:], []uint8{0xa3, 0x56, 0x41, 0x43, 0x74, 0x23, 0xe6, 0x26, 0xd9, 0x38, 0x25, 0x4a, 0x6b, 0x80, 0x49, 0x10, 0xa6, 0x67, 0xa, 0xc1})
142         ctx, cancel := context.WithTimeout(context.Background(), DefaultTrackerAnnounceTimeout)
143         defer cancel()
144         if dl, ok := t.Deadline(); ok {
145                 var cancel func()
146                 ctx, cancel = context.WithDeadline(context.Background(), dl.Add(-time.Second))
147                 defer cancel()
148         }
149         ar, err := Announce{
150                 TrackerUrl: trackers[0],
151                 Request:    req,
152                 Context:    ctx,
153         }.Do()
154         // Skip any net errors as we don't control the server.
155         var ne net.Error
156         if errors.As(err, &ne) {
157                 t.Skip(err)
158         }
159         require.NoError(t, err)
160         t.Log(ar)
161 }
162
163 func TestAnnounceRandomInfoHashThirdParty(t *testing.T) {
164         t.Parallel()
165         if testing.Short() {
166                 // This test involves contacting third party servers that may have
167                 // unpredictable results.
168                 t.SkipNow()
169         }
170         req := AnnounceRequest{
171                 Event: Stopped,
172         }
173         rand.Read(req.PeerId[:])
174         rand.Read(req.InfoHash[:])
175         wg := sync.WaitGroup{}
176         ctx, cancel := context.WithTimeout(context.Background(), DefaultTrackerAnnounceTimeout)
177         defer cancel()
178         if dl, ok := t.Deadline(); ok {
179                 var cancel func()
180                 ctx, cancel = context.WithDeadline(ctx, dl.Add(-time.Second))
181                 defer cancel()
182         }
183         for _, url := range trackers {
184                 wg.Add(1)
185                 go func(url string) {
186                         defer wg.Done()
187                         resp, err := Announce{
188                                 TrackerUrl: url,
189                                 Request:    req,
190                                 Context:    ctx,
191                         }.Do()
192                         if err != nil {
193                                 t.Logf("error announcing to %s: %s", url, err)
194                                 return
195                         }
196                         if resp.Leechers != 0 || resp.Seeders != 0 || len(resp.Peers) != 0 {
197                                 // The info hash we generated was random in 2^160 space. If we
198                                 // get a hit, something is weird.
199                                 t.Fatal(resp)
200                         }
201                         t.Logf("announced to %s", url)
202                         cancel()
203                 }(url)
204         }
205         wg.Wait()
206         cancel()
207 }
208
209 // Check that URLPath option is done correctly.
210 func TestURLPathOption(t *testing.T) {
211         conn, err := net.ListenUDP("udp", nil)
212         if err != nil {
213                 panic(err)
214         }
215         defer conn.Close()
216         announceErr := make(chan error)
217         go func() {
218                 _, err := Announce{
219                         TrackerUrl: (&url.URL{
220                                 Scheme: "udp",
221                                 Host:   conn.LocalAddr().String(),
222                                 Path:   "/announce",
223                         }).String(),
224                 }.Do()
225                 defer conn.Close()
226                 announceErr <- err
227         }()
228         var b [512]byte
229         _, addr, _ := conn.ReadFrom(b[:])
230         r := bytes.NewReader(b[:])
231         var h udp.RequestHeader
232         udp.Read(r, &h)
233         w := &bytes.Buffer{}
234         udp.Write(w, udp.ResponseHeader{
235                 Action:        udp.ActionConnect,
236                 TransactionId: h.TransactionId,
237         })
238         udp.Write(w, udp.ConnectionResponse{42})
239         conn.WriteTo(w.Bytes(), addr)
240         n, _, _ := conn.ReadFrom(b[:])
241         r = bytes.NewReader(b[:n])
242         udp.Read(r, &h)
243         udp.Read(r, &AnnounceRequest{})
244         all, _ := ioutil.ReadAll(r)
245         if string(all) != "\x02\x09/announce" {
246                 t.FailNow()
247         }
248         w = &bytes.Buffer{}
249         udp.Write(w, udp.ResponseHeader{
250                 Action:        udp.ActionAnnounce,
251                 TransactionId: h.TransactionId,
252         })
253         udp.Write(w, udp.AnnounceResponseHeader{})
254         conn.WriteTo(w.Bytes(), addr)
255         require.NoError(t, <-announceErr)
256 }