func (s *Server) setDefaults() (err error) { if s.id == "" { var id [20]byte h := crypto.SHA1.New() ss, err := os.Hostname() if err != nil { log.Print(err) } ss += s.socket.LocalAddr().String() h.Write([]byte(ss)) if b := h.Sum(id[:0:20]); len(b) != 20 { panic(len(b)) } if len(id) != 20 { panic(len(id)) } publicIP := func() net.IP { if s.config.PublicIP != nil { return s.config.PublicIP } else { return missinggo.AddrIP(s.socket.LocalAddr()) } }() SecureNodeId(id[:], publicIP) s.id = string(id[:]) } s.nodes = make(map[string]*node, maxNodes) return }
func (cl *Client) pieceHashed(t *Torrent, piece int, correct bool) { p := &t.pieces[piece] if p.EverHashed { // Don't score the first time a piece is hashed, it could be an // initial check. if correct { pieceHashedCorrect.Add(1) } else { log.Printf("%s: piece %d (%x) failed hash", t, piece, p.Hash) pieceHashedNotCorrect.Add(1) } } p.EverHashed = true touchers := cl.reapPieceTouches(t, piece) if correct { for _, c := range touchers { c.goodPiecesDirtied++ } err := p.Storage().MarkComplete() if err != nil { log.Printf("%T: error completing piece %d: %s", t.storage, piece, err) } t.updatePieceCompletion(piece) } else if len(touchers) != 0 { log.Printf("dropping and banning %d conns that touched piece", len(touchers)) for _, c := range touchers { c.badPiecesDirtied++ t.cl.banPeerIP(missinggo.AddrIP(c.remoteAddr())) t.dropConnection(c) } } cl.pieceChanged(t, piece) }
func (cl *Client) acceptConnections(l net.Listener, utp bool) { for { cl.waitAccept() conn, err := l.Accept() conn = pproffd.WrapNetConn(conn) if cl.closed.IsSet() { if conn != nil { conn.Close() } return } if err != nil { log.Print(err) // I think something harsher should happen here? Our accept // routine just f****d off. return } if utp { acceptUTP.Add(1) } else { acceptTCP.Add(1) } cl.mu.RLock() reject := cl.badPeerIPPort( missinggo.AddrIP(conn.RemoteAddr()), missinggo.AddrPort(conn.RemoteAddr())) cl.mu.RUnlock() if reject { acceptReject.Add(1) conn.Close() continue } go cl.incomingConnection(conn, utp) } }
func addClientPeer(t *Torrent, cl *Client) { t.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(cl.ListenAddr()), Port: missinggo.AddrPort(cl.ListenAddr()), }, }) }
func TestClientTransfer(t *testing.T) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) if err != nil { t.Fatal(err) } defer seeder.Close() seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(leecherDataDir) // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) { // return blob.TorrentData(info, leecherDataDir), nil // } blobStore := blob.NewStore(leecherDataDir) cfg.TorrentDataOpener = func(info *metainfo.Info) Data { return blobStore.OpenTorrent(info) } leecher, _ := NewClient(&cfg) defer leecher.Close() leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) // TODO: The piece state publishing is kinda jammed in here until I have a // more thorough test. go func() { s := leecherGreeting.pieceStateChanges.Subscribe() defer s.Close() for i := range s.Values { log.Print(i) } log.Print("finished") }() leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) r := leecherGreeting.NewReader() defer r.Close() _greeting, err := ioutil.ReadAll(r) if err != nil { t.Fatalf("%q %s", string(_greeting), err) } greeting := string(_greeting) if greeting != testutil.GreetingFileContents { t.Fatal(":(") } }
func TestServerDefaultNodeIdSecure(t *testing.T) { s, err := NewServer(nil) if err != nil { t.Fatal(err) } defer s.Close() if !NodeIdSecure(s.ID(), missinggo.AddrIP(s.Addr())) { t.Fatal("not secure") } }
func TestServerDefaultNodeIdSecure(t *testing.T) { s, err := NewServer(&ServerConfig{ NoDefaultBootstrap: true, }) require.NoError(t, err) defer s.Close() if !NodeIdSecure(s.ID(), missinggo.AddrIP(s.Addr())) { t.Fatal("not secure") } }
func TestDownloadOnDemand(t *testing.T) { layout, err := newGreetingLayout() require.NoError(t, err) defer layout.Destroy() seeder, err := torrent.NewClient(&torrent.Config{ DataDir: layout.Completed, DisableTrackers: true, NoDHT: true, ListenAddr: "localhost:0", Seed: true, // Ensure that the metainfo is obtained over the wire, since we added // the torrent to the seeder by magnet. DisableMetainfoCache: true, }) require.NoError(t, err) defer seeder.Close() testutil.ExportStatusWriter(seeder, "s") _, err = seeder.AddMagnet(fmt.Sprintf("magnet:?xt=urn:btih:%s", layout.Metainfo.Info.Hash.HexString())) require.NoError(t, err) leecher, err := torrent.NewClient(&torrent.Config{ DisableTrackers: true, NoDHT: true, ListenAddr: "localhost:0", DisableTCP: true, DefaultStorage: storage.NewMMap(filepath.Join(layout.BaseDir, "download")), // This can be used to check if clients can connect to other clients // with the same ID. // PeerID: seeder.PeerID(), }) require.NoError(t, err) testutil.ExportStatusWriter(leecher, "l") defer leecher.Close() leecherTorrent, _ := leecher.AddTorrent(layout.Metainfo) leecherTorrent.AddPeers([]torrent.Peer{ torrent.Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) fs := New(leecher) defer fs.Destroy() root, _ := fs.Root() node, _ := root.(fusefs.NodeStringLookuper).Lookup(netContext.Background(), "greeting") var attr fuse.Attr node.Attr(netContext.Background(), &attr) size := attr.Size resp := &fuse.ReadResponse{ Data: make([]byte, size), } node.(fusefs.HandleReader).Read(netContext.Background(), &fuse.ReadRequest{ Size: int(size), }, resp) assert.EqualValues(t, testutil.GreetingFileContents, resp.Data) }
// Writes the node info to its compact binary representation in b. See // CompactNodeInfoLen. func (ni *NodeInfo) PutCompact(b []byte) error { if n := copy(b[:], ni.ID[:]); n != 20 { panic(n) } ip := missinggo.AddrIP(ni.Addr).To4() if len(ip) != 4 { return errors.New("expected ipv4 address") } if n := copy(b[20:], ip); n != 4 { panic(n) } binary.BigEndian.PutUint16(b[24:], uint16(missinggo.AddrPort(ni.Addr))) return nil }
// Add response nodes to node table. func (s *Server) liftNodes(d Msg) { if d.Y != "r" { return } for _, cni := range d.R.Nodes { if missinggo.AddrPort(cni.Addr) == 0 { // TODO: Why would people even do this? continue } if s.ipBlocked(missinggo.AddrIP(cni.Addr)) { continue } n := s.getNode(cni.Addr, string(cni.ID[:])) n.SetIDFromBytes(cni.ID[:]) } }
func TestTorrentDroppedDuringResponsiveRead(t *testing.T) { seederDataDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(seederDataDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = seederDataDir seeder, err := NewClient(&cfg) require.Nil(t, err) defer seeder.Close() seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") require.Nil(t, err) defer os.RemoveAll(leecherDataDir) cfg = TestingConfig cfg.DataDir = leecherDataDir leecher, err := NewClient(&cfg) require.Nil(t, err) defer leecher.Close() leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) leecherTorrent.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) reader := leecherTorrent.NewReader() defer reader.Close() reader.SetReadahead(0) reader.SetResponsive() b := make([]byte, 2) _, err = reader.Seek(3, os.SEEK_SET) require.NoError(t, err) _, err = io.ReadFull(reader, b) assert.Nil(t, err) assert.EqualValues(t, "lo", string(b)) go leecherTorrent.Drop() _, err = reader.Seek(11, os.SEEK_SET) require.NoError(t, err) n, err := reader.Read(b) assert.EqualError(t, err, "torrent closed") assert.EqualValues(t, 0, n) }
func TestClientTransfer(t *testing.T) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) if err != nil { t.Fatal(err) } defer seeder.Close() seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(leecherDataDir) // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) { // return blob.TorrentData(info, leecherDataDir), nil // } cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent leecher, _ := NewClient(&cfg) defer leecher.Close() leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) r := leecherGreeting.NewReader() defer r.Close() _greeting, err := ioutil.ReadAll(r) if err != nil { t.Fatalf("%q %s", string(_greeting), err) } greeting := string(_greeting) if greeting != testutil.GreetingFileContents { t.Fatal(":(") } }
func (s *Server) writeToNode(b []byte, node dHTAddr) (err error) { if list := s.ipBlockList; list != nil { if r, ok := list.Lookup(missinggo.AddrIP(node.UDPAddr())); ok { err = fmt.Errorf("write to %s blocked: %s", node, r.Description) return } } n, err := s.socket.WriteTo(b, node.UDPAddr()) if err != nil { err = fmt.Errorf("error writing %d bytes to %s: %#v", len(b), node, err) return } if n != len(b) { err = io.ErrShortWrite return } return }
func connRemoteAddrIP(network, laddr string, dialHost string) net.IP { l, err := net.Listen(network, laddr) if err != nil { panic(err) } go func() { c, err := net.Dial(network, net.JoinHostPort(dialHost, fmt.Sprintf("%d", missinggo.AddrPort(l.Addr())))) if err != nil { panic(err) } defer c.Close() }() c, err := l.Accept() if err != nil { panic(err) } defer c.Close() ret := missinggo.AddrIP(c.RemoteAddr()) return ret }
func TestResponsive(t *testing.T) { seederDataDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(seederDataDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = seederDataDir seeder, err := NewClient(&cfg) require.Nil(t, err) defer seeder.Close() seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") require.Nil(t, err) defer os.RemoveAll(leecherDataDir) cfg = TestingConfig cfg.DataDir = leecherDataDir leecher, err := NewClient(&cfg) require.Nil(t, err) defer leecher.Close() leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) leecherTorrent.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) reader := leecherTorrent.NewReader() reader.SetReadahead(0) reader.SetResponsive() b := make([]byte, 2) _, err = reader.ReadAt(b, 3) assert.Nil(t, err) assert.EqualValues(t, "lo", string(b)) n, err := reader.ReadAt(b, 11) assert.Nil(t, err) assert.EqualValues(t, 2, n) assert.EqualValues(t, "d\n", string(b)) }
func (s *Server) serve() error { var b [0x10000]byte for { n, addr, err := s.socket.ReadFrom(b[:]) if err != nil { return err } read.Add(1) if n == len(b) { logonce.Stderr.Printf("received dht packet exceeds buffer size") continue } s.mu.Lock() blocked := s.ipBlocked(missinggo.AddrIP(addr)) s.mu.Unlock() if blocked { readBlocked.Add(1) continue } s.processPacket(b[:n], newDHTAddr(addr)) } }
func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) require.NoError(t, err) defer seeder.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(seeder, "s") } seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(leecherDataDir) fc, err := filecache.NewCache(leecherDataDir) require.NoError(t, err) if ps.SetLeecherStorageCapacity { fc.SetCapacity(ps.LeecherStorageCapacity) } cfg.DefaultStorage = storage.NewFileStorePieces(fc.AsFileStore()) cfg.DataDir = leecherDataDir leecher, _ := NewClient(&cfg) defer leecher.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(leecher, "l") } leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) require.NoError(t, err) assert.True(t, new) psc := leecherGreeting.SubscribePieceStateChanges() defer psc.Close() leecherGreeting.DownloadAll() if ps.Cancel { leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces()) } leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) completes := make(map[int]bool, 3) values: for { // started := time.Now() select { case _v := <-psc.Values: // log.Print(time.Since(started)) v := _v.(PieceStateChange) completes[v.Index] = v.Complete case <-time.After(100 * time.Millisecond): break values } } if ps.Cancel { assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes) } else { assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes) } }
// Check that after completing leeching, a leecher transitions to a seeding // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher. func TestSeedAfterDownloading(t *testing.T) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) require.NoError(t, err) defer seeder.Close() testutil.ExportStatusWriter(seeder, "s") seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) cfg.DataDir, err = ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(cfg.DataDir) leecher, err := NewClient(&cfg) require.NoError(t, err) defer leecher.Close() testutil.ExportStatusWriter(leecher, "l") cfg.Seed = false // cfg.TorrentDataOpener = nil cfg.DataDir, err = ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(cfg.DataDir) leecherLeecher, _ := NewClient(&cfg) defer leecherLeecher.Close() testutil.ExportStatusWriter(leecherLeecher, "ll") leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 3 return }()) // Simultaneously DownloadAll in Leecher, and read the contents // consecutively in LeecherLeecher. This non-deterministically triggered a // case where the leecher wouldn't unchoke the LeecherLeecher. var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() r := llg.NewReader() defer r.Close() b, err := ioutil.ReadAll(r) require.NoError(t, err) assert.EqualValues(t, testutil.GreetingFileContents, b) }() leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, Peer{ IP: missinggo.AddrIP(leecherLeecher.ListenAddr()), Port: missinggo.AddrPort(leecherLeecher.ListenAddr()), }, }) wg.Add(1) go func() { defer wg.Done() leecherGreeting.DownloadAll() leecher.WaitAll() }() wg.Wait() }
func testClientTransfer(t *testing.T, ps testClientTransferParams) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true if ps.SeederStorage != nil { cfg.DefaultStorage = ps.SeederStorage(greetingTempDir) } else { cfg.DataDir = greetingTempDir } seeder, err := NewClient(&cfg) require.NoError(t, err) defer seeder.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(seeder, "s") } _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) require.NoError(t, err) assert.True(t, new) leecherDataDir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(leecherDataDir) fc, err := filecache.NewCache(leecherDataDir) require.NoError(t, err) if ps.SetLeecherStorageCapacity { fc.SetCapacity(ps.LeecherStorageCapacity) } cfg.DefaultStorage = ps.LeecherFileCachePieceStorageFactory(fc) leecher, err := NewClient(&cfg) require.NoError(t, err) defer leecher.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(leecher, "l") } leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 ret.Storage = storage.NewFile(leecherDataDir) return }()) require.NoError(t, err) assert.True(t, new) leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) r := leecherGreeting.NewReader() defer r.Close() if ps.Responsive { r.SetResponsive() } if ps.SetReadahead { r.SetReadahead(ps.Readahead) } for range iter.N(2) { pos, err := r.Seek(0, os.SEEK_SET) assert.NoError(t, err) assert.EqualValues(t, 0, pos) _greeting, err := ioutil.ReadAll(r) assert.NoError(t, err) assert.EqualValues(t, testutil.GreetingFileContents, _greeting) } }
func newDHTAddr(addr net.Addr) dHTAddr { return cachedAddr{addr, addr.String(), missinggo.AddrIP(addr)} }