|
Packit |
8f70b4 |
/*
|
|
Packit |
8f70b4 |
* lftp - file transfer program
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* Copyright (c) 1996-2016 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8f70b4 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8f70b4 |
* the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
8f70b4 |
* (at your option) any later version.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8f70b4 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8f70b4 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8f70b4 |
* GNU General Public License for more details.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
8f70b4 |
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
8f70b4 |
*/
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include <config.h>
|
|
Packit |
8f70b4 |
#include <stdlib.h>
|
|
Packit |
8f70b4 |
#include <assert.h>
|
|
Packit |
8f70b4 |
#include <sys/types.h>
|
|
Packit |
8f70b4 |
#include <sys/stat.h>
|
|
Packit |
8f70b4 |
#include <sys/socket.h>
|
|
Packit |
8f70b4 |
#include <netinet/in.h>
|
|
Packit |
8f70b4 |
#include <arpa/inet.h>
|
|
Packit |
8f70b4 |
#include <unistd.h>
|
|
Packit |
8f70b4 |
#include <fcntl.h>
|
|
Packit |
8f70b4 |
#include <errno.h>
|
|
Packit |
8f70b4 |
#include <sha1.h>
|
|
Packit |
8f70b4 |
#include <dirent.h>
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include "Torrent.h"
|
|
Packit |
8f70b4 |
#include "TorrentTracker.h"
|
|
Packit |
8f70b4 |
#include "DHT.h"
|
|
Packit |
8f70b4 |
#include "log.h"
|
|
Packit |
8f70b4 |
#include "url.h"
|
|
Packit |
8f70b4 |
#include "misc.h"
|
|
Packit |
8f70b4 |
#include "plural.h"
|
|
Packit |
8f70b4 |
CDECL_BEGIN
|
|
Packit |
8f70b4 |
#include "human.h"
|
|
Packit |
8f70b4 |
CDECL_END
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static ResType torrent_vars[] = {
|
|
Packit |
8f70b4 |
{"torrent:port-range", "6881-6889", ResMgr::RangeValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
{"torrent:max-peers", "60", ResMgr::UNumberValidate},
|
|
Packit |
8f70b4 |
{"torrent:save-metadata", "yes", ResMgr::BoolValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
{"torrent:stop-min-ppr", "1.4", ResMgr::FloatValidate},
|
|
Packit |
8f70b4 |
{"torrent:stop-on-ratio", "2.0", ResMgr::FloatValidate},
|
|
Packit |
8f70b4 |
{"torrent:seed-max-time", "30d", ResMgr::TimeIntervalValidate},
|
|
Packit |
8f70b4 |
{"torrent:seed-min-peers", "3", ResMgr::UNumberValidate},
|
|
Packit |
8f70b4 |
{"torrent:ip", "", ResMgr::IPv4AddrValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
{"torrent:retracker", ""},
|
|
Packit |
8f70b4 |
{"torrent:use-dht", "yes", ResMgr::BoolValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
{"torrent:timeout", "7d", ResMgr::TimeIntervalValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
{"torrent:ipv6", "", ResMgr::IPv6AddrValidate, ResMgr::NoClosure},
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
{0}
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
static ResDecls torrent_vars_register(torrent_vars);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
#ifdef HAVE_IFADDRS_H
|
|
Packit |
8f70b4 |
#include <ifaddrs.h>
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
static const char *FindGlobalIPv6Address()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
#ifdef HAVE_IFADDRS_H
|
|
Packit |
8f70b4 |
struct ifaddrs *ifaddrs=0;
|
|
Packit |
8f70b4 |
getifaddrs(&ifaddrs);
|
|
Packit |
8f70b4 |
for(struct ifaddrs *ifa=ifaddrs; ifa; ifa=ifa->ifa_next) {
|
|
Packit |
8f70b4 |
if(ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET6) {
|
|
Packit |
8f70b4 |
struct in6_addr *addr=&((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr;
|
|
Packit |
8f70b4 |
if(!IN6_IS_ADDR_UNSPECIFIED(addr) && !IN6_IS_ADDR_LOOPBACK(addr)
|
|
Packit |
8f70b4 |
&& !IN6_IS_ADDR_LINKLOCAL(addr) && !IN6_IS_ADDR_SITELOCAL(addr)
|
|
Packit |
8f70b4 |
&& !IN6_IS_ADDR_MULTICAST(addr)) {
|
|
Packit |
8f70b4 |
char *buf=xstring::tmp_buf(INET6_ADDRSTRLEN);
|
|
Packit |
8f70b4 |
inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN);
|
|
Packit |
8f70b4 |
freeifaddrs(ifaddrs);
|
|
Packit |
8f70b4 |
return buf;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
freeifaddrs(ifaddrs);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ClassInit()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
static bool inited;
|
|
Packit |
8f70b4 |
if(inited)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
inited=true;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
const char *ipv6=ResMgr::Query("torrent:ipv6",0);
|
|
Packit |
8f70b4 |
if(!*ipv6)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
ipv6=FindGlobalIPv6Address();
|
|
Packit |
8f70b4 |
if(ipv6) {
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"found IPv6 address: %s",ipv6);
|
|
Packit |
8f70b4 |
ResMgr::Set("torrent:ipv6",0,ipv6);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring Torrent::my_peer_id;
|
|
Packit |
8f70b4 |
xstring Torrent::my_key;
|
|
Packit |
8f70b4 |
unsigned Torrent::my_key_num;
|
|
Packit |
8f70b4 |
xmap<Torrent*> Torrent::torrents;
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentListener> Torrent::listener;
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentListener> Torrent::listener_udp;
|
|
Packit |
8f70b4 |
SMTaskRef<DHT> Torrent::dht;
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentListener> Torrent::listener_ipv6;
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentListener> Torrent::listener_ipv6_udp;
|
|
Packit |
8f70b4 |
SMTaskRef<DHT> Torrent::dht_ipv6;
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
SMTaskRef<FDCache> Torrent::fd_cache;
|
|
Packit |
8f70b4 |
Ref<TorrentBlackList> Torrent::black_list;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StartDHT()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!ResMgr::QueryBool("torrent:use-dht",0)) {
|
|
Packit |
8f70b4 |
StopDHT();
|
|
Packit |
8f70b4 |
StopListenerUDP();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(dht)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
StartListenerUDP();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *home=get_lftp_cache_dir();
|
|
Packit |
8f70b4 |
const char *nodename=get_nodename();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
mkdir(xstring::format("%s/DHT",home),0700);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *ip=ResMgr::Query("torrent:ip",0);
|
|
Packit |
8f70b4 |
if(!ip || !ip[0])
|
|
Packit |
8f70b4 |
ip="127.0.0.1";
|
|
Packit |
8f70b4 |
sockaddr_compact ip_packed;
|
|
Packit |
8f70b4 |
ip_packed.get_space(4);
|
|
Packit |
8f70b4 |
inet_pton(AF_INET,ip,ip_packed.get_non_const());
|
|
Packit |
8f70b4 |
ip_packed.set_length(4);
|
|
Packit |
8f70b4 |
xstring node_id;
|
|
Packit |
8f70b4 |
DHT::MakeNodeId(node_id,ip_packed);
|
|
Packit |
8f70b4 |
dht=new DHT(AF_INET,node_id);
|
|
Packit |
8f70b4 |
dht->state_file.setf("%s/DHT/ipv4-%s",home,nodename);
|
|
Packit |
8f70b4 |
if(listener_udp->GetPort())
|
|
Packit |
8f70b4 |
dht->Load();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
ip=ResMgr::Query("torrent:ipv6",0);
|
|
Packit |
8f70b4 |
if(!ip || !ip[0])
|
|
Packit |
8f70b4 |
ip="::1";
|
|
Packit |
8f70b4 |
ip_packed.get_space(16);
|
|
Packit |
8f70b4 |
inet_pton(AF_INET6,ip,ip_packed.get_non_const());
|
|
Packit |
8f70b4 |
ip_packed.set_length(16);
|
|
Packit |
8f70b4 |
DHT::MakeNodeId(node_id,ip_packed);
|
|
Packit |
8f70b4 |
dht_ipv6=new DHT(AF_INET6,node_id);
|
|
Packit |
8f70b4 |
dht_ipv6->state_file.setf("%s/DHT/ipv6-%s",home,nodename);
|
|
Packit |
8f70b4 |
if(listener_ipv6_udp->GetPort())
|
|
Packit |
8f70b4 |
dht_ipv6->Load();
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::StopDHT()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!dht)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
dht->Save();
|
|
Packit |
8f70b4 |
dht=0;
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
dht_ipv6->Save();
|
|
Packit |
8f70b4 |
dht_ipv6=0;
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StartListener()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(listener)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
listener=new TorrentListener(AF_INET);
|
|
Packit |
8f70b4 |
listener->Roll(); // try to allocate ipv4 port first
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
listener_ipv6=new TorrentListener(AF_INET6);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::StopListener()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
listener=0;
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
listener_ipv6=0;
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::StartListenerUDP()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(listener_udp)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
listener_udp=new TorrentListener(AF_INET,SOCK_DGRAM);
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
listener_ipv6_udp=new TorrentListener(AF_INET6,SOCK_DGRAM);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::StopListenerUDP()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
listener_udp=0;
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
listener_ipv6_udp=0;
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Torrent::Torrent(const char *mf,const char *c,const char *od)
|
|
Packit |
8f70b4 |
: metainfo_url(mf),
|
|
Packit |
8f70b4 |
pieces_timer(10),
|
|
Packit |
8f70b4 |
cwd(c), output_dir(od), rate_limit(mf),
|
|
Packit |
8f70b4 |
seed_timer("torrent:seed-max-time",0),
|
|
Packit |
8f70b4 |
timeout_timer("torrent:timeout",0),
|
|
Packit |
8f70b4 |
optimistic_unchoke_timer(30), peers_scan_timer(1),
|
|
Packit |
8f70b4 |
am_interested_timer(1), shutting_down_timer(60),
|
|
Packit |
8f70b4 |
dht_announce_timer(10*60),
|
|
Packit |
8f70b4 |
dht_announce_count(0), dht_announce_count_ipv6(0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
shutting_down=false;
|
|
Packit |
8f70b4 |
complete=false;
|
|
Packit |
8f70b4 |
end_game=false;
|
|
Packit |
8f70b4 |
is_private=false;
|
|
Packit |
8f70b4 |
validating=false;
|
|
Packit |
8f70b4 |
force_valid=false;
|
|
Packit |
8f70b4 |
build_md=false;
|
|
Packit |
8f70b4 |
stop_if_complete=false;
|
|
Packit |
8f70b4 |
stop_if_known=false;
|
|
Packit |
8f70b4 |
md_saved=false;
|
|
Packit |
8f70b4 |
validate_index=0;
|
|
Packit |
8f70b4 |
metadata_size=0;
|
|
Packit |
8f70b4 |
info=0;
|
|
Packit |
8f70b4 |
pieces=0;
|
|
Packit |
8f70b4 |
piece_length=0;
|
|
Packit |
8f70b4 |
total_pieces=0;
|
|
Packit |
8f70b4 |
last_piece_length=0;
|
|
Packit |
8f70b4 |
total_length=0;
|
|
Packit |
8f70b4 |
total_sent=0;
|
|
Packit |
8f70b4 |
total_recv=0;
|
|
Packit |
8f70b4 |
total_left=0;
|
|
Packit |
8f70b4 |
complete_pieces=0;
|
|
Packit |
8f70b4 |
connected_peers_count=0;
|
|
Packit |
8f70b4 |
active_peers_count=0;
|
|
Packit |
8f70b4 |
complete_peers_count=0;
|
|
Packit |
8f70b4 |
am_interested_peers_count=0;
|
|
Packit |
8f70b4 |
am_not_choking_peers_count=0;
|
|
Packit |
8f70b4 |
max_peers=60;
|
|
Packit |
8f70b4 |
seed_min_peers=3;
|
|
Packit |
8f70b4 |
stop_on_ratio=2;
|
|
Packit |
8f70b4 |
stop_min_ppr=1,
|
|
Packit |
8f70b4 |
last_piece=TorrentPeer::NO_PIECE;
|
|
Packit |
8f70b4 |
min_piece_sources=0;
|
|
Packit |
8f70b4 |
avg_piece_sources=0;
|
|
Packit |
8f70b4 |
pieces_available_pct=0;
|
|
Packit |
8f70b4 |
current_min_ppr=0;
|
|
Packit |
8f70b4 |
current_max_ppr=0;
|
|
Packit |
8f70b4 |
Reconfig(0);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!my_peer_id) {
|
|
Packit |
8f70b4 |
my_peer_id.set("-lftp47-");
|
|
Packit |
8f70b4 |
my_peer_id.appendf("%04x",(unsigned)getpid() & 0xffff);
|
|
Packit |
8f70b4 |
my_peer_id.appendf("%08x",(unsigned)now.UnixTime());
|
|
Packit |
8f70b4 |
assert(my_peer_id.length()==PEER_ID_LEN);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!my_key) {
|
|
Packit |
8f70b4 |
for(int i=0; i<10; i++)
|
|
Packit |
8f70b4 |
my_key.appendf("%02x",unsigned(random()/13%256));
|
|
Packit |
8f70b4 |
my_key_num=random();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
dht_announce_timer.Stop();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Torrent::~Torrent()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::TrackersDone() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(shutting_down && shutting_down_timer.Stopped())
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(trackers[i]->IsActive())
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int Torrent::Done() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return (shutting_down && TrackersDone());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ShutdownTrackers() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
trackers[i]->Shutdown();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::Shutdown()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(shutting_down)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Enter(this);
|
|
Packit |
8f70b4 |
LogNote(3,"Shutting down...");
|
|
Packit |
8f70b4 |
shutting_down=true;
|
|
Packit |
8f70b4 |
shutting_down_timer.Reset();
|
|
Packit |
8f70b4 |
ShutdownTrackers();
|
|
Packit |
8f70b4 |
DenounceDHT();
|
|
Packit |
8f70b4 |
PrepareToDie();
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::AddTorrent(Torrent *t)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(FindTorrent(t->GetInfoHash()))
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(GetTorrentsCount()==0) {
|
|
Packit |
8f70b4 |
StartListener();
|
|
Packit |
8f70b4 |
StartDHT();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
torrents.add(t->GetInfoHash(),t);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::RemoveTorrent(Torrent *t)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(t!=FindTorrent(t->info_hash))
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
torrents.remove(t->GetInfoHash());
|
|
Packit |
8f70b4 |
if(GetTorrentsCount()==0) {
|
|
Packit |
8f70b4 |
StopListener();
|
|
Packit |
8f70b4 |
StopDHT();
|
|
Packit |
8f70b4 |
StopListenerUDP();
|
|
Packit |
8f70b4 |
fd_cache=0;
|
|
Packit |
8f70b4 |
black_list=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::PrepareToDie()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
metainfo_copy=0;
|
|
Packit |
8f70b4 |
building=0;
|
|
Packit |
8f70b4 |
peers.unset();
|
|
Packit |
8f70b4 |
if(info_hash && this==FindTorrent(info_hash))
|
|
Packit |
8f70b4 |
RemoveTorrent(this);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::SetError(Error *e)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(invalid_cause)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
invalid_cause=e;
|
|
Packit |
8f70b4 |
LogError(0,"%s: %s",
|
|
Packit |
8f70b4 |
invalid_cause->IsFatal()?"Fatal error":"Transient error",
|
|
Packit |
8f70b4 |
invalid_cause->Text());
|
|
Packit |
8f70b4 |
Shutdown();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::SetError(const char *msg)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(Error::Fatal(msg));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
double Torrent::GetRatio() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(total_sent==0 || total_length==total_left)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
return total_sent/double(total_length-total_left);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::SetDownloader(unsigned piece,unsigned block,const TorrentPeer *o,const TorrentPeer *n)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
piece_info[piece].set_downloader(block,o,n,BlocksInPiece(piece));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::AccountSend(unsigned p,unsigned len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
total_sent+=len;
|
|
Packit |
8f70b4 |
send_rate.Add(len);
|
|
Packit |
8f70b4 |
piece_info[p].add_ratio(float(len)/PieceLength(p));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::AccountRecv(unsigned p,unsigned len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
total_recv+=len;
|
|
Packit |
8f70b4 |
recv_rate.Add(len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *Torrent::Lookup(xmap_p<BeNode>& dict,const char *name,BeNode::be_type_t type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
BeNode *node=dict.lookup(name);
|
|
Packit |
8f70b4 |
if(!node) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("Meta-data: `%s' key missing",name));
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(node->type!=type) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("Meta-data: wrong `%s' type, must be %s",name,BeNode::TypeName(type)));
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return node;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::InitTranslation()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *charset="UTF-8"; // default
|
|
Packit |
8f70b4 |
recv_translate_utf8=new DirectedBuffer(DirectedBuffer::GET);
|
|
Packit |
8f70b4 |
recv_translate_utf8->SetTranslation(charset,true);
|
|
Packit |
8f70b4 |
if(metainfo_tree) {
|
|
Packit |
8f70b4 |
BeNode *b_charset=metainfo_tree->lookup("encoding",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
if(b_charset)
|
|
Packit |
8f70b4 |
charset=b_charset->str;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
recv_translate=new DirectedBuffer(DirectedBuffer::GET);
|
|
Packit |
8f70b4 |
recv_translate->SetTranslation(charset,true);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::TranslateString(BeNode *node) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(node->str_lc)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
recv_translate->ResetTranslation();
|
|
Packit |
8f70b4 |
recv_translate->PutTranslated(node->str);
|
|
Packit |
8f70b4 |
node->str_lc.nset(recv_translate->Get(),recv_translate->Size());
|
|
Packit |
8f70b4 |
recv_translate->Skip(recv_translate->Size());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::TranslateStringFromUTF8(BeNode *node) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(node->str_lc)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
const Ref<DirectedBuffer>& tr=recv_translate_utf8;
|
|
Packit |
8f70b4 |
tr->ResetTranslation();
|
|
Packit |
8f70b4 |
tr->PutTranslated(node->str);
|
|
Packit |
8f70b4 |
node->str_lc.nset(tr->Get(),tr->Size());
|
|
Packit |
8f70b4 |
tr->Skip(tr->Size());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::SHA1(const xstring& str,xstring& buf)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
buf.get_space(SHA1_DIGEST_SIZE);
|
|
Packit |
8f70b4 |
sha1_buffer(str.get(),str.length(),buf.get_non_const());
|
|
Packit |
8f70b4 |
buf.set_length(SHA1_DIGEST_SIZE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ValidatePiece(unsigned p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const xstring& buf=Torrent::RetrieveBlock(p,0,PieceLength(p));
|
|
Packit |
8f70b4 |
bool valid=false;
|
|
Packit |
8f70b4 |
if(buf.length()==PieceLength(p)) {
|
|
Packit |
8f70b4 |
xstring& sha1=xstring::get_tmp();
|
|
Packit |
8f70b4 |
SHA1(buf,sha1);
|
|
Packit |
8f70b4 |
if(building) {
|
|
Packit |
8f70b4 |
building->SetPiece(p,sha1);
|
|
Packit |
8f70b4 |
valid=true;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
valid=!memcmp(pieces->get()+p*SHA1_DIGEST_SIZE,sha1,SHA1_DIGEST_SIZE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!valid) {
|
|
Packit |
8f70b4 |
if(building) {
|
|
Packit |
8f70b4 |
SetError("File validation error");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(buf.length()==PieceLength(p))
|
|
Packit |
8f70b4 |
LogError(11,"piece %u digest mismatch",p);
|
|
Packit |
8f70b4 |
if(my_bitfield->get_bit(p)) {
|
|
Packit |
8f70b4 |
total_left+=PieceLength(p);
|
|
Packit |
8f70b4 |
complete_pieces--;
|
|
Packit |
8f70b4 |
my_bitfield->set_bit(p,0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetBlocksAbsent(p);
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
LogNote(11,"piece %u ok",p);
|
|
Packit |
8f70b4 |
if(!my_bitfield->get_bit(p)) {
|
|
Packit |
8f70b4 |
total_left-=PieceLength(p);
|
|
Packit |
8f70b4 |
complete_pieces++;
|
|
Packit |
8f70b4 |
my_bitfield->set_bit(p,1);
|
|
Packit |
8f70b4 |
piece_info[p].free_block_map();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
template<typename T>
|
|
Packit |
8f70b4 |
static inline int cmp(T a,T b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(a>b)
|
|
Packit |
8f70b4 |
return 1;
|
|
Packit |
8f70b4 |
if(a
|
|
Packit |
8f70b4 |
return -1;
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static Torrent *cmp_torrent;
|
|
Packit |
8f70b4 |
int Torrent::PiecesNeededCmp(const unsigned *a,const unsigned *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int ra=cmp_torrent->piece_info[*a].get_sources_count();
|
|
Packit |
8f70b4 |
int rb=cmp_torrent->piece_info[*b].get_sources_count();
|
|
Packit |
8f70b4 |
int c=cmp(ra,rb);
|
|
Packit |
8f70b4 |
if(c) return c;
|
|
Packit |
8f70b4 |
return cmp(*a,*b);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int Torrent::PeersCompareActivity(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
TimeDiff i1((*p1)->activity_timer.TimePassed());
|
|
Packit |
8f70b4 |
TimeDiff i2((*p2)->activity_timer.TimePassed());
|
|
Packit |
8f70b4 |
return cmp(i1.Seconds(),i2.Seconds());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int Torrent::PeersCompareRecvRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
float r1((*p1)->peer_recv_rate.Get());
|
|
Packit |
8f70b4 |
float r2((*p2)->peer_recv_rate.Get());
|
|
Packit |
8f70b4 |
int c=cmp(r1,r2);
|
|
Packit |
8f70b4 |
if(c) return c;
|
|
Packit |
8f70b4 |
return PeersCompareSendRate(p1,p2);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int Torrent::PeersCompareSendRate(const SMTaskRef<TorrentPeer> *p1,const SMTaskRef<TorrentPeer> *p2)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
float r1((*p1)->peer_send_rate.Get());
|
|
Packit |
8f70b4 |
float r2((*p2)->peer_send_rate.Get());
|
|
Packit |
8f70b4 |
return cmp(r1,r2);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::SeededEnough() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return (stop_on_ratio>0 && GetRatio()>=stop_on_ratio
|
|
Packit |
8f70b4 |
&& GetMinPerPieceRatio()>=stop_min_ppr)
|
|
Packit |
8f70b4 |
|| seed_timer.Stopped();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::CleanPeers()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
// remove uninteresting peers and request more
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
const TorrentPeer *peer=peers[i];
|
|
Packit |
8f70b4 |
if(peer->ActivityTimedOut()) {
|
|
Packit |
8f70b4 |
LogNote(4,"removing uninteresting peer %s (%s)",peer->GetName(),peers[i]->Status());
|
|
Packit |
8f70b4 |
BlackListPeer(peer,"2h");
|
|
Packit |
8f70b4 |
peers.remove(i--);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StartTrackers()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
trackers[i]->Start();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int Torrent::GetPort()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int port=0;
|
|
Packit |
8f70b4 |
if(listener && !port)
|
|
Packit |
8f70b4 |
port=listener->GetPort();
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(listener_ipv6 && !port)
|
|
Packit |
8f70b4 |
port=listener_ipv6->GetPort();
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
if(listener_udp && !port)
|
|
Packit |
8f70b4 |
return listener_udp->GetPort();
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(listener_ipv6_udp && !port)
|
|
Packit |
8f70b4 |
port=listener_ipv6_udp->GetPort();
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
return port;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::AnnounceDHT()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(is_private)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
CleanPeers();
|
|
Packit |
8f70b4 |
if(dht)
|
|
Packit |
8f70b4 |
dht->AnnouncePeer(this);
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(dht_ipv6)
|
|
Packit |
8f70b4 |
dht_ipv6->AnnouncePeer(this);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
dht_announce_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::DenounceDHT()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(is_private)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(dht)
|
|
Packit |
8f70b4 |
dht->DenouncePeer(this);
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(dht_ipv6)
|
|
Packit |
8f70b4 |
dht_ipv6->DenouncePeer(this);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::DHT_Announced(int af)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(af==AF_INET)
|
|
Packit |
8f70b4 |
dht_announce_count++;
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
else if(af==AF_INET6)
|
|
Packit |
8f70b4 |
dht_announce_count_ipv6++;
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *Torrent::DHT_Status() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!HasDHT() || Private())
|
|
Packit |
8f70b4 |
return "";
|
|
Packit |
8f70b4 |
static xstring status;
|
|
Packit |
8f70b4 |
status.nset("",0);
|
|
Packit |
8f70b4 |
if(dht_announce_count || dht_announce_count_ipv6) {
|
|
Packit |
8f70b4 |
status.append(_("announced via "));
|
|
Packit |
8f70b4 |
if(dht_announce_count)
|
|
Packit |
8f70b4 |
status.appendf("ipv4:%d",dht_announce_count);
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(dht_announce_count_ipv6) {
|
|
Packit |
8f70b4 |
if(dht_announce_count)
|
|
Packit |
8f70b4 |
status.append(", ");
|
|
Packit |
8f70b4 |
status.appendf("ipv6:%d",dht_announce_count_ipv6);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!dht_announce_timer.Stopped() && !validating) {
|
|
Packit |
8f70b4 |
if(status.length()>0)
|
|
Packit |
8f70b4 |
status.append("; ");
|
|
Packit |
8f70b4 |
status.appendf(_("next announce in %s"),
|
|
Packit |
8f70b4 |
dht_announce_timer.TimeLeft().toString(TimeInterval::TO_STR_TRANSLATE));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return status;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *Torrent::GetMetadataPath() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!QueryBool("torrent:save-metadata",0))
|
|
Packit |
8f70b4 |
return NULL;
|
|
Packit |
8f70b4 |
xstring& path=xstring::cat(get_lftp_data_dir(),"/torrent",NULL);
|
|
Packit |
8f70b4 |
mkdir(path,0700);
|
|
Packit |
8f70b4 |
path.append("/md");
|
|
Packit |
8f70b4 |
mkdir(path,0700);
|
|
Packit |
8f70b4 |
path.append('/');
|
|
Packit |
8f70b4 |
info_hash.hexdump_to(path);
|
|
Packit |
8f70b4 |
return path;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::SaveMetadata() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(md_saved)
|
|
Packit |
8f70b4 |
return true; // saved already.
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *path=GetMetadataPath();
|
|
Packit |
8f70b4 |
if(!path)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int fd=open(path,O_CREAT|O_WRONLY,0600);
|
|
Packit |
8f70b4 |
if(fd<0) {
|
|
Packit |
8f70b4 |
LogError(9,"open(%s): %s",path,strerror(errno));
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int bytes_to_write=metadata.length();
|
|
Packit |
8f70b4 |
int res=write(fd,metadata.get(),bytes_to_write);
|
|
Packit |
8f70b4 |
int saved_errno=errno;
|
|
Packit |
8f70b4 |
ftruncate(fd,bytes_to_write);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
close(fd);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(res==bytes_to_write)
|
|
Packit |
8f70b4 |
return true; // no error, fd is closed.
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(res<0)
|
|
Packit |
8f70b4 |
LogError(9,"write(%s): %s",path,strerror(saved_errno));
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
LogError(9,"write(%s): short write (only wrote %d bytes)",path,res);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool Torrent::LoadMetadata(const char *path)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int fd=open(path,O_RDONLY);
|
|
Packit |
8f70b4 |
if(fd<0) {
|
|
Packit |
8f70b4 |
LogError(9,"open(%s): %s",path,strerror(errno));
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
struct stat st;
|
|
Packit |
8f70b4 |
if(fstat(fd,&st)==-1) {
|
|
Packit |
8f70b4 |
close(fd);
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int bytes_to_read=st.st_size;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring md;
|
|
Packit |
8f70b4 |
int res=read(fd,md.add_space(bytes_to_read),bytes_to_read);
|
|
Packit |
8f70b4 |
int saved_errno=errno;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
close(fd);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(res==bytes_to_read) {
|
|
Packit |
8f70b4 |
// no error, fd is closed.
|
|
Packit |
8f70b4 |
md.add_commit(res);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring new_info_hash;
|
|
Packit |
8f70b4 |
SHA1(md,new_info_hash);
|
|
Packit |
8f70b4 |
if(info_hash && info_hash.ne(new_info_hash)) {
|
|
Packit |
8f70b4 |
LogError(9,"cached metadata does not match info_hash");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogNote(9,"got metadata from %s",path);
|
|
Packit |
8f70b4 |
if(SetMetadata(md)) {
|
|
Packit |
8f70b4 |
md_saved=true;
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(res<0)
|
|
Packit |
8f70b4 |
LogError(9,"read(%s): %s",path,strerror(saved_errno));
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
LogError(9,"read(%s): short read (only read %d bytes)",path,res);
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::FetchMetadataFromURL(const char *url)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
ParsedURL u(url,true);
|
|
Packit |
8f70b4 |
if(!u.proto) {
|
|
Packit |
8f70b4 |
u.proto.set("file");
|
|
Packit |
8f70b4 |
u.path.set(url); // undo %xx translation
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogNote(9,"Retrieving meta-data from `%s'...\n",url);
|
|
Packit |
8f70b4 |
FileCopyPeer *metainfo_src=new FileCopyPeerFA(&u,FA::RETRIEVE);
|
|
Packit |
8f70b4 |
FileCopyPeer *metainfo_dst=new FileCopyPeerMemory(10000000);
|
|
Packit |
8f70b4 |
metainfo_copy=new FileCopy(metainfo_src,metainfo_dst,false);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StartMetadataDownload()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *path=GetMetadataPath();
|
|
Packit |
8f70b4 |
if(path && access(path,R_OK)>=0) {
|
|
Packit |
8f70b4 |
// we have the metadata cached
|
|
Packit |
8f70b4 |
if(LoadMetadata(path)) {
|
|
Packit |
8f70b4 |
if(stop_if_known) {
|
|
Packit |
8f70b4 |
LogNote(2,"found cached metadata, stopping");
|
|
Packit |
8f70b4 |
Shutdown();
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
Startup();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
md_download.nset("",0);
|
|
Packit |
8f70b4 |
// add torrent without metadata to announce it and get peers to get MD from.
|
|
Packit |
8f70b4 |
AddTorrent(this);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::SetTotalLength(off_t tl)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
total_length=tl;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
LogNote(4,"Total length is %llu",total_length);
|
|
Packit |
8f70b4 |
total_left=total_length;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
last_piece_length=total_length%piece_length;
|
|
Packit |
8f70b4 |
if(last_piece_length==0)
|
|
Packit |
8f70b4 |
last_piece_length=piece_length;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
total_pieces=(total_length+piece_length-1)/piece_length;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
my_bitfield=new BitField(total_pieces);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
blocks_in_piece=(piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
|
|
Packit |
8f70b4 |
blocks_in_last_piece=(last_piece_length+BLOCK_SIZE-1)/BLOCK_SIZE;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
piece_info=new TorrentPiece[total_pieces]();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StartValidating()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
validate_index=0;
|
|
Packit |
8f70b4 |
validating=true;
|
|
Packit |
8f70b4 |
recv_rate.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::SetMetadata(const xstring& md)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
metadata.set(md);
|
|
Packit |
8f70b4 |
timeout_timer.Reset();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring new_info_hash;
|
|
Packit |
8f70b4 |
SHA1(metadata,new_info_hash);
|
|
Packit |
8f70b4 |
if(info_hash && info_hash.ne(new_info_hash)) {
|
|
Packit |
8f70b4 |
metadata.unset();
|
|
Packit |
8f70b4 |
SetError("metadata does not match info_hash");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
info_hash.set(new_info_hash);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!info) {
|
|
Packit |
8f70b4 |
int rest;
|
|
Packit |
8f70b4 |
info=BeNode::Parse(metadata,metadata.length(),&rest);
|
|
Packit |
8f70b4 |
if(!info) {
|
|
Packit |
8f70b4 |
SetError("cannot parse metadata");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
xmap_p<BeNode> d;
|
|
Packit |
8f70b4 |
d.add("info",info);
|
|
Packit |
8f70b4 |
metainfo_tree=new BeNode(&d);
|
|
Packit |
8f70b4 |
InitTranslation();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *b_piece_length=Lookup(info,"piece length",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(!b_piece_length || b_piece_length->num<1024 || b_piece_length->num>INT_MAX/4) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: invalid piece length");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
piece_length=b_piece_length->num;
|
|
Packit |
8f70b4 |
LogNote(4,"Piece length is %u",piece_length);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *b_name=info->lookup("name",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
BeNode *b_name_utf8=info->lookup("name.utf-8",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
if(b_name_utf8) {
|
|
Packit |
8f70b4 |
TranslateStringFromUTF8(b_name_utf8);
|
|
Packit |
8f70b4 |
name.set(b_name_utf8->str_lc);
|
|
Packit |
8f70b4 |
} else if(b_name) {
|
|
Packit |
8f70b4 |
TranslateString(b_name);
|
|
Packit |
8f70b4 |
name.set(b_name->str_lc);
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
name.truncate();
|
|
Packit |
8f70b4 |
info_hash.hexdump_to(name);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Reconfig(0);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *files=info->lookup("files");
|
|
Packit |
8f70b4 |
if(!files) {
|
|
Packit |
8f70b4 |
BeNode *length=Lookup(info,"length",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(!length || length->num<0) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: invalid or missing length");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
total_length=length->num;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
if(files->type!=BeNode::BE_LIST) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: wrong `info/files' type, must be LIST");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
total_length=0;
|
|
Packit |
8f70b4 |
for(int i=0; i<files->list.length(); i++) {
|
|
Packit |
8f70b4 |
if(files->list[i]->type!=BeNode::BE_DICT) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("Meta-data: wrong `info/files[%d]' type, must be LIST",i));
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
BeNode *f=Lookup(files->list[i]->dict,"length",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(!f || f->num<0) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: invalid or missing file length");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!Lookup(files->list[i]->dict,"path",BeNode::BE_LIST)) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: file path missing");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
total_length+=f->num;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
this->files=new TorrentFiles(files,this);
|
|
Packit |
8f70b4 |
SetTotalLength(total_length);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *b_pieces=Lookup(info,"pieces",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
if(!b_pieces) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: `pieces' missing");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
pieces=&b_pieces->str;
|
|
Packit |
8f70b4 |
if(pieces->length()!=SHA1_DIGEST_SIZE*total_pieces) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: invalid `pieces' length");
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
is_private=info->lookup_int("private");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::Startup()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!info_hash || !metadata)
|
|
Packit |
8f70b4 |
SetError("missing metadata");
|
|
Packit |
8f70b4 |
if(shutting_down)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Torrent *other=FindTorrent(info_hash);
|
|
Packit |
8f70b4 |
if(other) {
|
|
Packit |
8f70b4 |
if(other!=this) {
|
|
Packit |
8f70b4 |
SetError("This torrent is already running");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
AddTorrent(this);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!building)
|
|
Packit |
8f70b4 |
md_saved=SaveMetadata();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!force_valid && !building) {
|
|
Packit |
8f70b4 |
StartValidating();
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
my_bitfield->set_range(0,total_pieces,1);
|
|
Packit |
8f70b4 |
complete_pieces=total_pieces;
|
|
Packit |
8f70b4 |
total_left=0;
|
|
Packit |
8f70b4 |
complete=true;
|
|
Packit |
8f70b4 |
seed_timer.Reset();
|
|
Packit |
8f70b4 |
dht_announce_timer.Stop();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
RestartPeers();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::RestartPeers()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
peers[i]->Restart();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
static int base32_char_to_value(char c)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(c>='a' && c<='z')
|
|
Packit |
8f70b4 |
return c-'a';
|
|
Packit |
8f70b4 |
if(c>='A' && c<='Z')
|
|
Packit |
8f70b4 |
return c-'A';
|
|
Packit |
8f70b4 |
if(c>='2' && c<='7')
|
|
Packit |
8f70b4 |
return c-'2'+26;
|
|
Packit |
8f70b4 |
if(c=='=')
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
return -1;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void base32_decode(const char *base32,xstring& out)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unsigned data=0;
|
|
Packit |
8f70b4 |
int data_bits=0;
|
|
Packit |
8f70b4 |
int pad_bits=0;
|
|
Packit |
8f70b4 |
while(*base32) {
|
|
Packit |
8f70b4 |
char c=*base32++;
|
|
Packit |
8f70b4 |
if(c=='=' && data_bits<=pad_bits)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(pad_bits>0 && c!='=')
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
int v=base32_char_to_value(c);
|
|
Packit |
8f70b4 |
if(v==-1)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
data|=((v&31)<<(11-data_bits));
|
|
Packit |
8f70b4 |
data_bits+=5;
|
|
Packit |
8f70b4 |
if(c=='=')
|
|
Packit |
8f70b4 |
pad_bits+=5;
|
|
Packit |
8f70b4 |
if(data_bits>=8) {
|
|
Packit |
8f70b4 |
out.append(char((data>>8)&255));
|
|
Packit |
8f70b4 |
data<<=8;
|
|
Packit |
8f70b4 |
data_bits-=8;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(data_bits>0)
|
|
Packit |
8f70b4 |
out.append(char((data>>8)&255));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ParseMagnet(const char *m0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char *m=alloca_strdup(m0);
|
|
Packit |
8f70b4 |
for(char *p=strtok(m,"&";; p; p=strtok(NULL,"&")) {
|
|
Packit |
8f70b4 |
char *v=strchr(p,'=');
|
|
Packit |
8f70b4 |
if(!v)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
*v++=0;
|
|
Packit |
8f70b4 |
v=xstring::get_tmp(v).url_decode(URL_DECODE_PLUS).get_non_const();
|
|
Packit |
8f70b4 |
if(!strcmp(p,"xt")) {
|
|
Packit |
8f70b4 |
if(strncmp(v,"urn:btih:",9)) {
|
|
Packit |
8f70b4 |
SetError("Only BitTorrent magnet links are supported");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
v+=9;
|
|
Packit |
8f70b4 |
xstring& btih=xstring::get_tmp(v);
|
|
Packit |
8f70b4 |
if(btih.length()==40) {
|
|
Packit |
8f70b4 |
btih.hex_decode();
|
|
Packit |
8f70b4 |
if(btih.length()!=20) {
|
|
Packit |
8f70b4 |
SetError("Invalid value of urn:btih in magnet link");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
info_hash.move_here(btih);
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
info_hash.truncate();
|
|
Packit |
8f70b4 |
base32_decode(v,info_hash);
|
|
Packit |
8f70b4 |
if(info_hash.length()!=20) {
|
|
Packit |
8f70b4 |
info_hash.unset();
|
|
Packit |
8f70b4 |
SetError("Invalid value of urn:btih in magnet link");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(!strcmp(p,"tr")) {
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentTracker> new_tracker(new TorrentTracker(this,v));
|
|
Packit |
8f70b4 |
if(!new_tracker->Failed()) {
|
|
Packit |
8f70b4 |
new_tracker->tracker_no=trackers.count();
|
|
Packit |
8f70b4 |
trackers.append(new_tracker.borrow());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(!strcmp(p,"dn")) {
|
|
Packit |
8f70b4 |
name.set(v);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!info_hash) {
|
|
Packit |
8f70b4 |
SetError("missing urn:btih in magnet link");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(FindTorrent(info_hash)) {
|
|
Packit |
8f70b4 |
SetError("This torrent is already running");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
StartMetadataDownload();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::CalcPiecesStats()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
min_piece_sources=INT_MAX;
|
|
Packit |
8f70b4 |
avg_piece_sources=0;
|
|
Packit |
8f70b4 |
pieces_available_pct=0;
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
if(my_bitfield->get_bit(i))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
unsigned sc=piece_info[i].get_sources_count();
|
|
Packit |
8f70b4 |
if(min_piece_sources>sc)
|
|
Packit |
8f70b4 |
min_piece_sources=sc;
|
|
Packit |
8f70b4 |
if(sc==0)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
pieces_available_pct++;
|
|
Packit |
8f70b4 |
avg_piece_sources+=sc;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
avg_piece_sources=avg_piece_sources*256/(total_pieces-complete_pieces);
|
|
Packit |
8f70b4 |
pieces_available_pct=pieces_available_pct*100/(total_pieces-complete_pieces);
|
|
Packit |
8f70b4 |
CalcPerPieceRatio();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::CalcPerPieceRatio()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
current_min_ppr=1024;
|
|
Packit |
8f70b4 |
current_max_ppr=0;
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
float ppr=piece_info[i].get_ratio();
|
|
Packit |
8f70b4 |
if(current_min_ppr>ppr)
|
|
Packit |
8f70b4 |
current_min_ppr=ppr;
|
|
Packit |
8f70b4 |
if(current_max_ppr
|
|
Packit |
8f70b4 |
current_max_ppr=ppr;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::RebuildPiecesNeeded()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
pieces_needed.truncate();
|
|
Packit |
8f70b4 |
bool enter_end_game=true;
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
if(!my_bitfield->get_bit(i)) {
|
|
Packit |
8f70b4 |
if(!piece_info[i].has_a_downloader())
|
|
Packit |
8f70b4 |
enter_end_game=false;
|
|
Packit |
8f70b4 |
if(piece_info[i].has_no_sources())
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
pieces_needed.append(i);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
piece_info[i].cleanup();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!end_game && enter_end_game) {
|
|
Packit |
8f70b4 |
LogNote(1,"entering End Game mode");
|
|
Packit |
8f70b4 |
end_game=true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
cmp_torrent=this;
|
|
Packit |
8f70b4 |
pieces_needed.qsort(PiecesNeededCmp);
|
|
Packit |
8f70b4 |
CalcPiecesStats();
|
|
Packit |
8f70b4 |
pieces_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
TorrentBuild::TorrentBuild(const char *path) :
|
|
Packit |
8f70b4 |
top_path(path),
|
|
Packit |
8f70b4 |
name(basename_ptr(path)),
|
|
Packit |
8f70b4 |
done(false),
|
|
Packit |
8f70b4 |
total_length(0),
|
|
Packit |
8f70b4 |
piece_length(0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
name.rtrim('/');
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
struct stat st;
|
|
Packit |
8f70b4 |
if(stat(path,&st)==-1) {
|
|
Packit |
8f70b4 |
error=SysError();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(S_ISREG(st.st_mode)) {
|
|
Packit |
8f70b4 |
total_length=st.st_size;
|
|
Packit |
8f70b4 |
LogNote(10,"single file %s, size %lld",path,(long long)st.st_size);
|
|
Packit |
8f70b4 |
Finish();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!S_ISDIR(st.st_mode)) {
|
|
Packit |
8f70b4 |
error=new Error(-1,"Need a plain file or directory",true);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
QueueDir("");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *TorrentBuild::lc_to_utf8(const char *s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!translate || !s)
|
|
Packit |
8f70b4 |
return s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
translate->ResetTranslation();
|
|
Packit |
8f70b4 |
translate->PutTranslated(s);
|
|
Packit |
8f70b4 |
int len;
|
|
Packit |
8f70b4 |
translate->Get(&s,&len;;
|
|
Packit |
8f70b4 |
translate->Skip(len);
|
|
Packit |
8f70b4 |
return xstring::get_tmp(s,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentBuild::Finish()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
done=true;
|
|
Packit |
8f70b4 |
LogNote(10,"scan finished, total_length=%lld",(long long)total_length);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
translate=new DirectedBuffer(DirectedBuffer::PUT);
|
|
Packit |
8f70b4 |
translate->SetTranslation("UTF-8",false);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xmap_p<BeNode> *b_info=new xmap_p<BeNode>();
|
|
Packit |
8f70b4 |
b_info->add("name",new BeNode(lc_to_utf8(name)));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
piece_length=16*1024;
|
|
Packit |
8f70b4 |
off_t length_scan=piece_length*2200;
|
|
Packit |
8f70b4 |
while(length_scan<=total_length) {
|
|
Packit |
8f70b4 |
piece_length*=2;
|
|
Packit |
8f70b4 |
length_scan*=2;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
b_info->add("piece length",new BeNode(piece_length));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(files.count()==0) {
|
|
Packit |
8f70b4 |
b_info->add("length",new BeNode(total_length));
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
files.Sort(FileSet::BYNAME);
|
|
Packit |
8f70b4 |
files.rewind();
|
|
Packit |
8f70b4 |
xarray_p<BeNode> *b_files=new xarray_p<BeNode>();
|
|
Packit |
8f70b4 |
for(FileInfo *fi=files.curr(); fi; fi=files.next()) {
|
|
Packit |
8f70b4 |
xarray_p<BeNode> *path=new xarray_p<BeNode>();
|
|
Packit |
8f70b4 |
const char *name_utf8=lc_to_utf8(fi->name);
|
|
Packit |
8f70b4 |
char *p=alloca_strdup(name_utf8);
|
|
Packit |
8f70b4 |
for(p=strtok(p,"/"); p; p=strtok(NULL,"/"))
|
|
Packit |
8f70b4 |
path->append(new BeNode(p));
|
|
Packit |
8f70b4 |
xmap_p<BeNode> *b_file=new xmap_p<BeNode>();
|
|
Packit |
8f70b4 |
b_file->add("path",new BeNode(path));
|
|
Packit |
8f70b4 |
b_file->add("length",new BeNode(fi->size));
|
|
Packit |
8f70b4 |
b_files->append(new BeNode(b_file));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
b_info->add("files",new BeNode(b_files));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
info=new BeNode(b_info);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentBuild::SetPiece(unsigned p,const xstring& sha)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
assert(pieces.length()==p*20); // require sequential building
|
|
Packit |
8f70b4 |
pieces.append(sha);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const xstring& TorrentBuild::GetMetadata()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
info->dict.add("pieces",new BeNode(pieces));
|
|
Packit |
8f70b4 |
return info->Pack();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const xstring& TorrentBuild::Status() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(Done())
|
|
Packit |
8f70b4 |
return xstring::get_tmp("");
|
|
Packit |
8f70b4 |
const char *curr=CurrPath();
|
|
Packit |
8f70b4 |
int count=files.count();
|
|
Packit |
8f70b4 |
if(curr[0])
|
|
Packit |
8f70b4 |
return xstring::format(plural("%d file$|s$ found, now scanning %s",count),count,curr);
|
|
Packit |
8f70b4 |
return xstring::format(plural("%d file$|s$ found",count),count);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int TorrentBuild::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(Done())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *curr_path=CurrPath();
|
|
Packit |
8f70b4 |
if(!curr_path) {
|
|
Packit |
8f70b4 |
Finish();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *full_path=dir_file(top_path,curr_path);
|
|
Packit |
8f70b4 |
full_path=alloca_strdup(full_path);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
DIR *dir=opendir(full_path);
|
|
Packit |
8f70b4 |
if(!dir) {
|
|
Packit |
8f70b4 |
if(NonFatalError(errno))
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(dirs_to_scan.Count()<=1)
|
|
Packit |
8f70b4 |
error=SysError();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
LogError(0,"opendir(%s): %s",full_path,strerror(errno));
|
|
Packit |
8f70b4 |
NextDir();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogNote(10,"scanning %s",full_path);
|
|
Packit |
8f70b4 |
struct dirent *entry;
|
|
Packit |
8f70b4 |
while((entry=readdir(dir))!=0) {
|
|
Packit |
8f70b4 |
if(!strcmp(entry->d_name,".") || !strcmp(entry->d_name,".."))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
struct stat st;
|
|
Packit |
8f70b4 |
const char *path=dir_file(full_path,entry->d_name);
|
|
Packit |
8f70b4 |
if(lstat(path,&st)==-1) {
|
|
Packit |
8f70b4 |
LogError(0,"stat(%s): %s",path,strerror(errno));
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(S_ISREG(st.st_mode))
|
|
Packit |
8f70b4 |
AddFile(dir_file(curr_path,entry->d_name),&st);
|
|
Packit |
8f70b4 |
else if(S_ISDIR(st.st_mode))
|
|
Packit |
8f70b4 |
QueueDir(dir_file(curr_path,entry->d_name));
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
LogNote(10,"ignoring %s (not a directory nor a plain file)",path);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
closedir(dir);
|
|
Packit |
8f70b4 |
NextDir();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentBuild::AddFile(const char *path,struct stat *st)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileInfo *fi=new FileInfo(path);
|
|
Packit |
8f70b4 |
fi->SetSize(st->st_size);
|
|
Packit |
8f70b4 |
files.Add(fi);
|
|
Packit |
8f70b4 |
total_length+=st->st_size;
|
|
Packit |
8f70b4 |
LogNote(10,"adding %s, size %lld",path,(long long)fi->size);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int Torrent::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(Done() || shutting_down)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(!complete && !building && timeout_timer.Stopped()) {
|
|
Packit |
8f70b4 |
SetError("timed out with no progress");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(build_md && !files) {
|
|
Packit |
8f70b4 |
if(!building)
|
|
Packit |
8f70b4 |
building=new TorrentBuild(metainfo_url);
|
|
Packit |
8f70b4 |
if(!building->Done())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
if(building->Failed()) {
|
|
Packit |
8f70b4 |
SetError(building->ErrorText());
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
InitTranslation();
|
|
Packit |
8f70b4 |
name.set(building->name);
|
|
Packit |
8f70b4 |
piece_length=building->piece_length;
|
|
Packit |
8f70b4 |
SetTotalLength(building->total_length);
|
|
Packit |
8f70b4 |
files=new TorrentFiles(building->GetFilesNode(),this);
|
|
Packit |
8f70b4 |
output_dir.set(building->GetBaseDirectory());
|
|
Packit |
8f70b4 |
StartValidating();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!metainfo_tree && metainfo_url && !md_download && !build_md) {
|
|
Packit |
8f70b4 |
// retrieve metainfo if don't have already.
|
|
Packit |
8f70b4 |
if(!metainfo_copy) {
|
|
Packit |
8f70b4 |
if(metainfo_url.begins_with("magnet:?")) {
|
|
Packit |
8f70b4 |
ParseMagnet(metainfo_url+8);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(metainfo_url.length()==40 && strspn(metainfo_url,"0123456789ABCDEFabcdef")==40
|
|
Packit |
8f70b4 |
&& access(metainfo_url,0)==-1) {
|
|
Packit |
8f70b4 |
xstring& btih=xstring::get_tmp(metainfo_url);
|
|
Packit |
8f70b4 |
btih.hex_decode();
|
|
Packit |
8f70b4 |
assert(btih.length()==20);
|
|
Packit |
8f70b4 |
info_hash.move_here(btih);
|
|
Packit |
8f70b4 |
if(FindTorrent(info_hash)) {
|
|
Packit |
8f70b4 |
SetError("This torrent is already running");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
StartMetadataDownload();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
FetchMetadataFromURL(metainfo_url);
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(metainfo_copy->Error()) {
|
|
Packit |
8f70b4 |
SetError(Error::Fatal(metainfo_copy->ErrorText()));
|
|
Packit |
8f70b4 |
metainfo_copy=0;
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!metainfo_copy->Done())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
LogNote(9,"meta-data EOF\n");
|
|
Packit |
8f70b4 |
int rest;
|
|
Packit |
8f70b4 |
const char *metainfo_buf;
|
|
Packit |
8f70b4 |
int metainfo_len;
|
|
Packit |
8f70b4 |
metainfo_copy->put->Get(&metainfo_buf,&metainfo_len);
|
|
Packit |
8f70b4 |
metainfo_tree=BeNode::Parse(metainfo_buf,metainfo_len,&rest);
|
|
Packit |
8f70b4 |
metainfo_copy=0;
|
|
Packit |
8f70b4 |
if(!metainfo_tree) {
|
|
Packit |
8f70b4 |
SetError("Meta-data parse error");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(rest>0) {
|
|
Packit |
8f70b4 |
SetError("Junk at the end of Meta-data");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
InitTranslation();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
LogNote(10,"Received meta-data:");
|
|
Packit |
8f70b4 |
Log::global->Write(10,metainfo_tree->Format());
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(metainfo_tree->type!=BeNode::BE_DICT) {
|
|
Packit |
8f70b4 |
SetError("Meta-data: wrong top level type, must be DICT");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *retracker=ResMgr::Query("torrent:retracker",GetName());
|
|
Packit |
8f70b4 |
int retracker_len=xstrlen(retracker);
|
|
Packit |
8f70b4 |
BeNode *announce_list=metainfo_tree->lookup("announce-list",BeNode::BE_LIST);
|
|
Packit |
8f70b4 |
if(announce_list) {
|
|
Packit |
8f70b4 |
for(int i=0; i<announce_list->list.length(); i++) {
|
|
Packit |
8f70b4 |
BeNode *announce_list1=announce_list->list[i];
|
|
Packit |
8f70b4 |
if(announce_list1->type!=BeNode::BE_LIST)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentTracker> new_tracker;
|
|
Packit |
8f70b4 |
for(int j=0; j<announce_list1->list.length(); j++) {
|
|
Packit |
8f70b4 |
BeNode *announce=announce_list1->list[j];
|
|
Packit |
8f70b4 |
if(announce->type!=BeNode::BE_STR)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(retracker_len && !strncmp(retracker,announce->str,retracker_len))
|
|
Packit |
8f70b4 |
retracker=0, retracker_len=0;
|
|
Packit |
8f70b4 |
if(!new_tracker)
|
|
Packit |
8f70b4 |
new_tracker=new TorrentTracker(this,announce->str);
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
new_tracker->AddURL(announce->str);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(new_tracker && !new_tracker->Failed()) {
|
|
Packit |
8f70b4 |
new_tracker->tracker_no=trackers.count();
|
|
Packit |
8f70b4 |
trackers.append(new_tracker.borrow());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(trackers.count()==0) {
|
|
Packit |
8f70b4 |
const xstring& announce=metainfo_tree->lookup_str("announce");
|
|
Packit |
8f70b4 |
if(announce) {
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentTracker> new_tracker(
|
|
Packit |
8f70b4 |
new TorrentTracker(this,announce));
|
|
Packit |
8f70b4 |
if(!new_tracker->Failed())
|
|
Packit |
8f70b4 |
trackers.append(new_tracker.borrow());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(retracker_len) {
|
|
Packit |
8f70b4 |
SMTaskRef<TorrentTracker> new_tracker(new TorrentTracker(this,retracker));
|
|
Packit |
8f70b4 |
if(!new_tracker->Failed()) {
|
|
Packit |
8f70b4 |
new_tracker->tracker_no=trackers.count();
|
|
Packit |
8f70b4 |
trackers.append(new_tracker.borrow());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
BeNode *nodes=metainfo_tree->lookup("nodes",BeNode::BE_LIST);
|
|
Packit |
8f70b4 |
if(nodes && dht) {
|
|
Packit |
8f70b4 |
for(int i=0; i<nodes->list.count(); i++) {
|
|
Packit |
8f70b4 |
BeNode *n=nodes->list[i];
|
|
Packit |
8f70b4 |
if(n->type!=BeNode::BE_LIST || n->list.count()<2)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
BeNode *b_host=n->list[0];
|
|
Packit |
8f70b4 |
BeNode *b_port=n->list[1];
|
|
Packit |
8f70b4 |
if(b_host->type!=BeNode::BE_STR || b_port->type!=BeNode::BE_INT)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(b_port->num<=0 || b_port->num>=65535)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
ParsedURL u;
|
|
Packit |
8f70b4 |
u.host.set(b_host->str);
|
|
Packit |
8f70b4 |
u.port.set(xstring::format("%u",(unsigned)b_port->num));
|
|
Packit |
8f70b4 |
xstring_ca node(u.Combine());
|
|
Packit |
8f70b4 |
dht->AddBootstrapNode(node);
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
dht_ipv6->AddBootstrapNode(node);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
info=Lookup(metainfo_tree,"info",BeNode::BE_DICT);
|
|
Packit |
8f70b4 |
if(!info)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(SetMetadata(info->str))
|
|
Packit |
8f70b4 |
Startup();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
if(Done())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(peers_scan_timer.Stopped())
|
|
Packit |
8f70b4 |
ScanPeers();
|
|
Packit |
8f70b4 |
if(validating) {
|
|
Packit |
8f70b4 |
ValidatePiece(validate_index++);
|
|
Packit |
8f70b4 |
if(validate_index
|
|
Packit |
8f70b4 |
recv_rate.Add(piece_length);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
recv_rate.Add(last_piece_length);
|
|
Packit |
8f70b4 |
validating=false;
|
|
Packit |
8f70b4 |
recv_rate.Reset();
|
|
Packit |
8f70b4 |
if(total_left==0) {
|
|
Packit |
8f70b4 |
complete=true;
|
|
Packit |
8f70b4 |
seed_timer.Reset();
|
|
Packit |
8f70b4 |
if(stop_if_complete) {
|
|
Packit |
8f70b4 |
LogNote(2,"torrent is already complete, stopping");
|
|
Packit |
8f70b4 |
Shutdown();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(building) {
|
|
Packit |
8f70b4 |
if(!complete) {
|
|
Packit |
8f70b4 |
SetError("File validation error");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!SetMetadata(building->GetMetadata()))
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
building=0;
|
|
Packit |
8f70b4 |
xstring magnet("magnet:?xt=urn:btih:");
|
|
Packit |
8f70b4 |
magnet.append(info_hash.hexdump());
|
|
Packit |
8f70b4 |
magnet.appendf("&xl=%lld",(long long)total_length);
|
|
Packit |
8f70b4 |
magnet.append("&dn=");
|
|
Packit |
8f70b4 |
magnet.append_url_encoded(name,URL_PATH_UNSAFE);
|
|
Packit |
8f70b4 |
printf("%s\n",magnet.get());
|
|
Packit |
8f70b4 |
Startup();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
RestartPeers();
|
|
Packit |
8f70b4 |
dht_announce_timer.Stop();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(GetPort())
|
|
Packit |
8f70b4 |
StartTrackers();
|
|
Packit |
8f70b4 |
if(dht_announce_timer.Stopped())
|
|
Packit |
8f70b4 |
AnnounceDHT();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// count peers
|
|
Packit |
8f70b4 |
connected_peers_count=0;
|
|
Packit |
8f70b4 |
active_peers_count=0;
|
|
Packit |
8f70b4 |
complete_peers_count=0;
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
connected_peers_count+=peers[i]->Connected();
|
|
Packit |
8f70b4 |
active_peers_count+=peers[i]->Active();
|
|
Packit |
8f70b4 |
complete_peers_count+=peers[i]->Complete();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!metadata)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(optimistic_unchoke_timer.Stopped())
|
|
Packit |
8f70b4 |
OptimisticUnchoke();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// rebuild lists of needed pieces
|
|
Packit |
8f70b4 |
if(!complete && (pieces_needed.count()==0 || pieces_timer.Stopped()))
|
|
Packit |
8f70b4 |
RebuildPiecesNeeded();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(complete) {
|
|
Packit |
8f70b4 |
if(pieces_timer.Stopped()) {
|
|
Packit |
8f70b4 |
CalcPerPieceRatio();
|
|
Packit |
8f70b4 |
pieces_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(SeededEnough()) {
|
|
Packit |
8f70b4 |
Shutdown();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::BlackListPeer(const TorrentPeer *peer,const char *timeout)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(peer->IsPassive() || GetTorrentsCount()==0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(!black_list)
|
|
Packit |
8f70b4 |
black_list=new TorrentBlackList();
|
|
Packit |
8f70b4 |
black_list->Add(peer->GetAddress(),timeout);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool Torrent::BlackListed(const TorrentPeer *peer)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return black_list && black_list->Listed(peer->GetAddress());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::CanAccept() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return !validating && decline_timer.Stopped();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::Accept(int s,const sockaddr_u *addr,IOBuffer *rb)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!CanAccept()) {
|
|
Packit |
8f70b4 |
LogNote(4,"declining new connection");
|
|
Packit |
8f70b4 |
Delete(rb);
|
|
Packit |
8f70b4 |
close(s);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
TorrentPeer *p=new TorrentPeer(this,addr,TorrentPeer::TR_ACCEPTED);
|
|
Packit |
8f70b4 |
p->Connect(s,rb);
|
|
Packit |
8f70b4 |
AddPeer(p);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::AddPeer(TorrentPeer *peer)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(BlackListed(peer)) {
|
|
Packit |
8f70b4 |
Delete(peer);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(peers[i]->AddressEq(peer)) {
|
|
Packit |
8f70b4 |
if(peer->Connected() && !peers[i]->Connected()) {
|
|
Packit |
8f70b4 |
peers[i]=peer;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
Delete(peer);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
peers.append(peer);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int Torrent::GetWantedPeersCount() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int numwant=complete?seed_min_peers:max_peers/2;
|
|
Packit |
8f70b4 |
if(numwant>peers.count())
|
|
Packit |
8f70b4 |
numwant-=peers.count();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
numwant=0;
|
|
Packit |
8f70b4 |
if(shutting_down)
|
|
Packit |
8f70b4 |
numwant=-1;
|
|
Packit |
8f70b4 |
if(numwant>1) {
|
|
Packit |
8f70b4 |
// count almost ready for request trackers
|
|
Packit |
8f70b4 |
int trackers_count=0;
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
trackers_count+=(trackers[i]->tracker_timer.TimeLeft()<60);
|
|
Packit |
8f70b4 |
// request peers from all trackers evenly (round up).
|
|
Packit |
8f70b4 |
if(trackers_count)
|
|
Packit |
8f70b4 |
numwant=(numwant+trackers_count-1)/trackers_count;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return numwant;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *Torrent::MakePath(BeNode *p) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
BeNode *path=p->lookup("path.utf-8",BeNode::BE_LIST);
|
|
Packit |
8f70b4 |
void (Torrent::*tr)(BeNode*)const=&Torrent::TranslateStringFromUTF8;
|
|
Packit |
8f70b4 |
if(!path) {
|
|
Packit |
8f70b4 |
path=p->lookup("path",BeNode::BE_LIST);
|
|
Packit |
8f70b4 |
tr=&Torrent::TranslateString;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
static xstring buf;
|
|
Packit |
8f70b4 |
buf.set(name);
|
|
Packit |
8f70b4 |
if(buf.eq("..") || buf[0]=='/') {
|
|
Packit |
8f70b4 |
buf.set_substr(0,0,"_",1);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
for(int i=0; i<path->list.count(); i++) {
|
|
Packit |
8f70b4 |
BeNode *e=path->list[i];
|
|
Packit |
8f70b4 |
if(e->type==BeNode::BE_STR) {
|
|
Packit |
8f70b4 |
(this->*tr)(e);
|
|
Packit |
8f70b4 |
buf.append('/');
|
|
Packit |
8f70b4 |
if(e->str_lc.eq(".."))
|
|
Packit |
8f70b4 |
buf.append('_');
|
|
Packit |
8f70b4 |
buf.append(e->str_lc);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return buf;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *Torrent::FindFileByPosition(unsigned piece,unsigned begin,off_t *f_pos,off_t *f_tail) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
off_t target_pos=(off_t)piece*piece_length+begin;
|
|
Packit |
8f70b4 |
TorrentFile *file=files->FindByPosition(target_pos);
|
|
Packit |
8f70b4 |
if(!file)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
*f_pos=target_pos-file->pos;
|
|
Packit |
8f70b4 |
*f_tail=file->length-*f_pos;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return file->path;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
TorrentFiles::TorrentFiles(const BeNode *files,const Torrent *t)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!files) {
|
|
Packit |
8f70b4 |
grow_space(1);
|
|
Packit |
8f70b4 |
set_length(1);
|
|
Packit |
8f70b4 |
file(0)->set(t->GetName(),0,t->TotalLength());
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
int count=files->list.length();
|
|
Packit |
8f70b4 |
grow_space(count);
|
|
Packit |
8f70b4 |
set_length(count);
|
|
Packit |
8f70b4 |
off_t scan_pos=0;
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
BeNode *node=files->list[i];
|
|
Packit |
8f70b4 |
off_t file_length=node->lookup_int("length");
|
|
Packit |
8f70b4 |
file(i)->set(t->MakePath(node),scan_pos,file_length);
|
|
Packit |
8f70b4 |
scan_pos+=file_length;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
qsort(pos_cmp);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
TorrentFile *TorrentFiles::FindByPosition(off_t pos)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int i=0;
|
|
Packit |
8f70b4 |
int j=length()-1;
|
|
Packit |
8f70b4 |
while(i<=j) {
|
|
Packit |
8f70b4 |
// invariant: the target element is in the range [i,j]
|
|
Packit |
8f70b4 |
int m=(i+j)/2;
|
|
Packit |
8f70b4 |
if(file(m)->contains_pos(pos))
|
|
Packit |
8f70b4 |
return file(m);
|
|
Packit |
8f70b4 |
if(file(m)->pos>pos)
|
|
Packit |
8f70b4 |
j=m-1;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
i=m+1;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FDCache::FDCache()
|
|
Packit |
8f70b4 |
: clean_timer(1)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
max_count=16;
|
|
Packit |
8f70b4 |
max_time=30;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
FDCache::~FDCache()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
CloseAll();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void FDCache::Clean()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i<3; i++) {
|
|
Packit |
8f70b4 |
xmap<FD>& cache=this->cache[i];
|
|
Packit |
8f70b4 |
for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
|
|
Packit |
8f70b4 |
if(f->fd==-1) {
|
|
Packit |
8f70b4 |
if(f->last_used+1
|
|
Packit |
8f70b4 |
cache.remove(cache.each_key());
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(f->last_used+max_time
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"closing %s",cache.each_key().get());
|
|
Packit |
8f70b4 |
close(f->fd);
|
|
Packit |
8f70b4 |
cache.remove(cache.each_key());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
while(Count()>max_count && CloseOne())
|
|
Packit |
8f70b4 |
/*empty*/;
|
|
Packit |
8f70b4 |
if(Count()>0)
|
|
Packit |
8f70b4 |
clean_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int FDCache::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(clean_timer.Stopped())
|
|
Packit |
8f70b4 |
Clean();
|
|
Packit |
8f70b4 |
return STALL;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void FDCache::Close(const char *name)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const xstring &n=xstring::get_tmp(name);
|
|
Packit |
8f70b4 |
for(int i=0; i<3; i++) {
|
|
Packit |
8f70b4 |
const FD& f=cache[i].lookup(n);
|
|
Packit |
8f70b4 |
if(f.last_used!=0) {
|
|
Packit |
8f70b4 |
if(f.fd!=-1) {
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"closing %s",name);
|
|
Packit |
8f70b4 |
#ifdef HAVE_POSIX_FADVISE
|
|
Packit |
8f70b4 |
if(i==O_RDONLY) // avoid filling up the cache
|
|
Packit |
8f70b4 |
posix_fadvise(f.fd,0,0,POSIX_FADV_DONTNEED);
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
close(f.fd);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
cache[i].remove(n);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void FDCache::CloseAll()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i<3; i++) {
|
|
Packit |
8f70b4 |
xmap<FD>& cache=this->cache[i];
|
|
Packit |
8f70b4 |
for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
|
|
Packit |
8f70b4 |
if(f->fd!=-1) {
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"closing %s",cache.each_key().get());
|
|
Packit |
8f70b4 |
close(f->fd);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
cache.remove(cache.each_key());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool FDCache::CloseOne()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int oldest_mode=0;
|
|
Packit |
8f70b4 |
int oldest_fd=-1;
|
|
Packit |
8f70b4 |
int oldest_time=0;
|
|
Packit |
8f70b4 |
const xstring *oldest_key=0;
|
|
Packit |
8f70b4 |
for(int i=0; i<3; i++) {
|
|
Packit |
8f70b4 |
xmap<FD>& cache=this->cache[i];
|
|
Packit |
8f70b4 |
for(const FD *f=&cache.each_begin(); f->last_used; f=&cache.each_next()) {
|
|
Packit |
8f70b4 |
if(f->fd==-1)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(oldest_key==0 || f->last_used
|
|
Packit |
8f70b4 |
oldest_key=&cache.each_key();
|
|
Packit |
8f70b4 |
oldest_time=f->last_used;
|
|
Packit |
8f70b4 |
oldest_fd=f->fd;
|
|
Packit |
8f70b4 |
oldest_mode=i;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!oldest_key)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
if(oldest_fd!=-1) {
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"closing %s",oldest_key->get());
|
|
Packit |
8f70b4 |
close(oldest_fd);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
cache[oldest_mode].remove(*oldest_key);
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int FDCache::Count() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return cache[0].count()+cache[1].count()+cache[2].count();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int FDCache::OpenFile(const char *file,int m,off_t size)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int ci=m&3;
|
|
Packit |
8f70b4 |
assert(ci<3);
|
|
Packit |
8f70b4 |
FD& f=cache[ci].lookup_Lv(file);
|
|
Packit |
8f70b4 |
if(f.last_used!=0) {
|
|
Packit |
8f70b4 |
if(f.fd!=-1)
|
|
Packit |
8f70b4 |
f.last_used=now.UnixTime();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
errno=f.saved_errno;
|
|
Packit |
8f70b4 |
return f.fd;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(ci==O_RDONLY) {
|
|
Packit |
8f70b4 |
// O_RDWR also will do, check if we have it.
|
|
Packit |
8f70b4 |
const FD& f_rw=cache[O_RDWR].lookup(file);
|
|
Packit |
8f70b4 |
if(f_rw.last_used!=0 && f_rw.fd!=-1) {
|
|
Packit |
8f70b4 |
// don't update last_used to expire it and reopen with proper mode
|
|
Packit |
8f70b4 |
return f_rw.fd;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Clean();
|
|
Packit |
8f70b4 |
clean_timer.Reset();
|
|
Packit |
8f70b4 |
ProtoLog::LogNote(9,"opening %s",file);
|
|
Packit |
8f70b4 |
int fd;
|
|
Packit |
8f70b4 |
do {
|
|
Packit |
8f70b4 |
fd=open(file,m,0664);
|
|
Packit |
8f70b4 |
} while(fd==-1 && (errno==EMFILE || errno==ENFILE) && CloseOne());
|
|
Packit |
8f70b4 |
FD new_entry = {fd,errno,now.UnixTime()};
|
|
Packit |
8f70b4 |
cache[ci].add(file,new_entry);
|
|
Packit |
8f70b4 |
if(fd!=-1)
|
|
Packit |
8f70b4 |
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
|
Packit |
8f70b4 |
if(fd==-1 || size==0)
|
|
Packit |
8f70b4 |
return fd;
|
|
Packit |
8f70b4 |
if(ci==O_RDWR && QueryBool("file:use-fallocate",0)) {
|
|
Packit |
8f70b4 |
struct stat st;
|
|
Packit |
8f70b4 |
// check if it is newly created file, then allocate space
|
|
Packit |
8f70b4 |
if(fstat(fd,&st)!=-1 && st.st_size==0) {
|
|
Packit |
8f70b4 |
if(lftp_fallocate(fd,size)==-1 && errno!=ENOSYS && errno!=EOPNOTSUPP) {
|
|
Packit |
8f70b4 |
ProtoLog::LogError(9,"space allocation for %s (%lld bytes) failed: %s",
|
|
Packit |
8f70b4 |
file,(long long)size,strerror(errno));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#ifdef HAVE_POSIX_FADVISE
|
|
Packit |
8f70b4 |
if(ci==O_RDONLY) {
|
|
Packit |
8f70b4 |
// validation mode (when validating, size>0)
|
|
Packit |
8f70b4 |
posix_fadvise(fd,0,size,POSIX_FADV_SEQUENTIAL);
|
|
Packit |
8f70b4 |
posix_fadvise(fd,0,size,POSIX_FADV_NOREUSE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif//HAVE_POSIX_FADVISE
|
|
Packit |
8f70b4 |
return fd;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int Torrent::OpenFile(const char *file,int m,off_t size)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bool did_mkdir=false;
|
|
Packit |
8f70b4 |
if(!fd_cache)
|
|
Packit |
8f70b4 |
fd_cache=new FDCache();
|
|
Packit |
8f70b4 |
try_again:
|
|
Packit |
8f70b4 |
const char *cf=dir_file(output_dir,file);
|
|
Packit |
8f70b4 |
int fd=fd_cache->OpenFile(cf,m,size);
|
|
Packit |
8f70b4 |
while(fd==-1 && (errno==EMFILE || errno==ENFILE) && peers.count()>0) {
|
|
Packit |
8f70b4 |
peers.chop(); // free an fd
|
|
Packit |
8f70b4 |
fd=fd_cache->OpenFile(cf,m,size);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(validating)
|
|
Packit |
8f70b4 |
return fd;
|
|
Packit |
8f70b4 |
if(fd==-1)
|
|
Packit |
8f70b4 |
fd_cache->Close(cf); // remove negative cache.
|
|
Packit |
8f70b4 |
if(fd==-1 && errno==ENOENT && !did_mkdir) {
|
|
Packit |
8f70b4 |
LogError(10,"open(%s): %s",cf,strerror(errno));
|
|
Packit |
8f70b4 |
const char *sl=strchr(file,'/');
|
|
Packit |
8f70b4 |
while(sl)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(sl>file) {
|
|
Packit |
8f70b4 |
if(mkdir(cf=dir_file(output_dir,xstring::get_tmp(file,sl-file)),0775)==-1 && errno!=EEXIST)
|
|
Packit |
8f70b4 |
LogError(9,"mkdir(%s): %s",cf,strerror(errno));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
sl=strchr(sl+1,'/');
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
did_mkdir=true;
|
|
Packit |
8f70b4 |
goto try_again;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return fd;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::CloseFile(const char *file) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!fd_cache)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
fd_cache->Close(dir_file(output_dir,file));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::SetPieceNotWanted(unsigned piece)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int j=0; j
|
|
Packit |
8f70b4 |
if(pieces_needed[j]==piece) {
|
|
Packit |
8f70b4 |
pieces_needed.remove(j);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#define MIN(a,b) ((a)<(b)?(a):(b))
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::StoreBlock(unsigned piece,unsigned begin,unsigned len,const char *buf,TorrentPeer *src_peer)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
peers[i]->CancelBlock(piece,begin);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
unsigned b=begin/BLOCK_SIZE;
|
|
Packit |
8f70b4 |
int bc=(len+BLOCK_SIZE-1)/BLOCK_SIZE;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
off_t f_pos=0;
|
|
Packit |
8f70b4 |
off_t f_rest=len;
|
|
Packit |
8f70b4 |
while(len>0) {
|
|
Packit |
8f70b4 |
const char *file=FindFileByPosition(piece,begin,&f_pos,&f_rest);
|
|
Packit |
8f70b4 |
int fd=OpenFile(file,O_RDWR|O_CREAT,f_pos+f_rest);
|
|
Packit |
8f70b4 |
if(fd==-1) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("open(%s): %s",file,strerror(errno)));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int w=pwrite(fd,buf,MIN(f_rest,len),f_pos);
|
|
Packit |
8f70b4 |
int saved_errno=errno;
|
|
Packit |
8f70b4 |
if(w==-1) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("pwrite(%s): %s",file,strerror(saved_errno)));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(w==0) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("pwrite(%s): write error - disk full?",file));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
buf+=w;
|
|
Packit |
8f70b4 |
begin+=w;
|
|
Packit |
8f70b4 |
len-=w;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
while(bc-->0) {
|
|
Packit |
8f70b4 |
SetBlockPresent(piece,b++);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(AllBlocksPresent(piece) && !my_bitfield->get_bit(piece)) {
|
|
Packit |
8f70b4 |
ValidatePiece(piece);
|
|
Packit |
8f70b4 |
if(!my_bitfield->get_bit(piece)) {
|
|
Packit |
8f70b4 |
LogError(0,"new piece %u digest mismatch",piece);
|
|
Packit |
8f70b4 |
src_peer->MarkPieceInvalid(piece);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogNote(3,"piece %u complete",piece);
|
|
Packit |
8f70b4 |
timeout_timer.Reset();
|
|
Packit |
8f70b4 |
SetPieceNotWanted(piece);
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
peers[i]->Have(piece);
|
|
Packit |
8f70b4 |
if(my_bitfield->has_all_set() && !complete) {
|
|
Packit |
8f70b4 |
complete=true;
|
|
Packit |
8f70b4 |
seed_timer.Reset();
|
|
Packit |
8f70b4 |
end_game=false;
|
|
Packit |
8f70b4 |
ScanPeers();
|
|
Packit |
8f70b4 |
SendTrackersRequest("completed");
|
|
Packit |
8f70b4 |
recv_rate.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::SendTrackersRequest(const char *event) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(!trackers[i]->Failed())
|
|
Packit |
8f70b4 |
trackers[i]->SendTrackerRequest(event);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const xstring& Torrent::RetrieveBlock(unsigned piece,unsigned begin,unsigned len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
static xstring buf;
|
|
Packit |
8f70b4 |
buf.truncate(0);
|
|
Packit |
8f70b4 |
buf.get_space(len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
off_t f_pos=0;
|
|
Packit |
8f70b4 |
off_t f_rest=len;
|
|
Packit |
8f70b4 |
while(len>0) {
|
|
Packit |
8f70b4 |
const char *file=FindFileByPosition(piece,begin,&f_pos,&f_rest);
|
|
Packit |
8f70b4 |
int fd=OpenFile(file,O_RDONLY,validating?f_pos+f_rest:0);
|
|
Packit |
8f70b4 |
if(fd==-1)
|
|
Packit |
8f70b4 |
return xstring::null;
|
|
Packit |
8f70b4 |
int w=pread(fd,buf.add_space(len),MIN(f_rest,len),f_pos);
|
|
Packit |
8f70b4 |
if(w==-1) {
|
|
Packit |
8f70b4 |
SetError(xstring::format("pread(%s): %s",file,strerror(errno)));
|
|
Packit |
8f70b4 |
return xstring::null;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(w==0) {
|
|
Packit |
8f70b4 |
// buf.append_padding(len,'\0');
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
buf.add_commit(w);
|
|
Packit |
8f70b4 |
begin+=w;
|
|
Packit |
8f70b4 |
len-=w;
|
|
Packit |
8f70b4 |
if(validating && w==f_rest)
|
|
Packit |
8f70b4 |
CloseFile(file);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return buf;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
TorrentPeer *Torrent::FindPeerById(const xstring& p_id)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// linear search - peers count<100, called rarely
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
const TorrentPeer *peer=peers[i];
|
|
Packit |
8f70b4 |
if(peer->peer_id.eq(p_id))
|
|
Packit |
8f70b4 |
return const_cast<TorrentPeer*>(peer);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ScanPeers() {
|
|
Packit |
8f70b4 |
// scan existing peers
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
const TorrentPeer *peer=peers[i];
|
|
Packit |
8f70b4 |
const char *blacklist_time="2h";
|
|
Packit |
8f70b4 |
if(peer->Failed()) {
|
|
Packit |
8f70b4 |
LogError(2,"peer %s failed: %s",peer->GetName(),peer->ErrorText());
|
|
Packit |
8f70b4 |
} else if(peer->Disconnected() && peer->ActivityTimedOut()) {
|
|
Packit |
8f70b4 |
LogNote(4,"peer %s disconnected",peer->GetName());
|
|
Packit |
8f70b4 |
} else if(peer->myself) {
|
|
Packit |
8f70b4 |
LogNote(4,"removing myself-connected peer %s",peer->GetName());
|
|
Packit |
8f70b4 |
blacklist_time="forever";
|
|
Packit |
8f70b4 |
} else if(peer->duplicate) {
|
|
Packit |
8f70b4 |
LogNote(4,"removing duplicate peer %s",peer->GetName());
|
|
Packit |
8f70b4 |
} else if(complete && peer->Seed()) {
|
|
Packit |
8f70b4 |
LogNote(4,"removing unneeded peer %s (%s)",peer->GetName(),peers[i]->Status());
|
|
Packit |
8f70b4 |
blacklist_time="1d";
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
// keep the peer.
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(blacklist_time)
|
|
Packit |
8f70b4 |
BlackListPeer(peer,blacklist_time);
|
|
Packit |
8f70b4 |
peers.remove(i--);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ReducePeers();
|
|
Packit |
8f70b4 |
peers_scan_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::ReducePeers()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(max_peers>0 && peers.count()>max_peers) {
|
|
Packit |
8f70b4 |
// remove least interesting peers.
|
|
Packit |
8f70b4 |
peers.qsort(PeersCompareActivity);
|
|
Packit |
8f70b4 |
int to_remove=peers.count()-max_peers;
|
|
Packit |
8f70b4 |
while(to_remove-->0) {
|
|
Packit |
8f70b4 |
TimeInterval max_idle(peers.last()->activity_timer.TimePassed());
|
|
Packit |
8f70b4 |
LogNote(3,"removing peer %s (too many; idle:%s)",peers.last()->GetName(),
|
|
Packit |
8f70b4 |
max_idle.toString(TimeInterval::TO_STR_TERSE+TimeInterval::TO_STR_TRANSLATE));
|
|
Packit |
8f70b4 |
peers.chop();
|
|
Packit |
8f70b4 |
if(max_idle<60)
|
|
Packit |
8f70b4 |
decline_timer.Set(60-max_idle.Seconds());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
peers.qsort(complete ? PeersCompareSendRate : PeersCompareRecvRate);
|
|
Packit |
8f70b4 |
ReduceUploaders();
|
|
Packit |
8f70b4 |
ReduceDownloaders();
|
|
Packit |
8f70b4 |
UnchokeBestUploaders();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::ReduceUploaders()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bool rate_low = RateLow(RateLimit::GET);
|
|
Packit |
8f70b4 |
if(am_interested_peers_count < (rate_low?max_uploaders:min_uploaders+1))
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
// make the slowest uninterested
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
TorrentPeer *peer=peers[i].get_non_const();
|
|
Packit |
8f70b4 |
if(peer->am_interested) {
|
|
Packit |
8f70b4 |
if(peer->interest_timer.TimePassed() <= 30)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
peer->SetAmInterested(false);
|
|
Packit |
8f70b4 |
if(am_interested_peers_count < max_uploaders)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::ReduceDownloaders()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bool rate_low = RateLow(RateLimit::PUT);
|
|
Packit |
8f70b4 |
if(am_not_choking_peers_count < (rate_low?max_downloaders:min_downloaders+1))
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
// choke the slowest
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
TorrentPeer *peer=peers[i].get_non_const();
|
|
Packit |
8f70b4 |
if(!peer->am_choking && peer->peer_interested) {
|
|
Packit |
8f70b4 |
if(peer->choke_timer.TimePassed() <= 30)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
peer->SetAmChoking(true);
|
|
Packit |
8f70b4 |
if(am_not_choking_peers_count < max_downloaders)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool Torrent::NeedMoreUploaders()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!metadata || validating)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
return RateLow(RateLimit::GET) && am_interested_peers_count < max_uploaders
|
|
Packit |
8f70b4 |
&& am_interested_timer.Stopped();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool Torrent::AllowMoreDownloaders()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!metadata || validating)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
return RateLow(RateLimit::PUT) && am_not_choking_peers_count < max_downloaders;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::UnchokeBestUploaders()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!metadata)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// unchoke 4 best uploaders
|
|
Packit |
8f70b4 |
int limit = 4;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int count=0;
|
|
Packit |
8f70b4 |
for(int i=peers.count()-1; i>=0 && count
|
|
Packit |
8f70b4 |
TorrentPeer *peer=peers[i].get_non_const();
|
|
Packit |
8f70b4 |
if(!peer->Connected())
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(!peer->choke_timer.Stopped())
|
|
Packit |
8f70b4 |
continue; // cannot change choke status yet
|
|
Packit |
8f70b4 |
if(!peer->peer_interested)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
peer->SetAmChoking(false);
|
|
Packit |
8f70b4 |
count++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::OptimisticUnchoke()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xarray<TorrentPeer*> choked_peers;
|
|
Packit |
8f70b4 |
for(int i=peers.count()-1; i>=0; i--) {
|
|
Packit |
8f70b4 |
TorrentPeer *peer=peers[i].get_non_const();
|
|
Packit |
8f70b4 |
if(!peer->Connected())
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(!peer->choke_timer.Stopped())
|
|
Packit |
8f70b4 |
continue; // cannot change choke status yet
|
|
Packit |
8f70b4 |
if(peer->am_choking) {
|
|
Packit |
8f70b4 |
if(!peer->peer_interested) {
|
|
Packit |
8f70b4 |
peer->SetAmChoking(false);
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
choked_peers.append(peer);
|
|
Packit |
8f70b4 |
if(peer->retry_timer.TimePassed()<60) {
|
|
Packit |
8f70b4 |
// newly connected is more likely to be unchoked
|
|
Packit |
8f70b4 |
choked_peers.append(peer);
|
|
Packit |
8f70b4 |
choked_peers.append(peer);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(choked_peers.count()==0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
choked_peers[rand()/13%choked_peers.count()]->SetAmChoking(false);
|
|
Packit |
8f70b4 |
optimistic_unchoke_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int Torrent::PeerBytesAllowed(const TorrentPeer *peer,RateLimit::dir_t dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
float peer_rate=(dir==RateLimit::GET ? peer->peer_send_rate : peer->peer_recv_rate).Get();
|
|
Packit |
8f70b4 |
float total_rate=(dir==RateLimit::GET ? send_rate : recv_rate).Get();
|
|
Packit |
8f70b4 |
const int min_rate = 1024;
|
|
Packit |
8f70b4 |
// the more is the opposite rate the more rate allowed, with a minimum
|
|
Packit |
8f70b4 |
float bytes = rate_limit.BytesAllowed(dir);
|
|
Packit |
8f70b4 |
bytes *= (peer_rate + min_rate)
|
|
Packit |
8f70b4 |
/ (total_rate + active_peers_count*min_rate);
|
|
Packit |
8f70b4 |
return (int)bytes;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::PeerBytesUsed(int b,RateLimit::dir_t dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
rate_limit.BytesUsed(b,dir);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void Torrent::Reconfig(const char *name)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *c=GetName();
|
|
Packit |
8f70b4 |
max_peers=ResMgr::Query("torrent:max-peers",c);
|
|
Packit |
8f70b4 |
seed_min_peers=ResMgr::Query("torrent:seed-min-peers",c);
|
|
Packit |
8f70b4 |
stop_on_ratio=ResMgr::Query("torrent:stop-on-ratio",c);
|
|
Packit |
8f70b4 |
stop_min_ppr=ResMgr::Query("torrent:stop-min-ppr",c);
|
|
Packit |
8f70b4 |
rate_limit.Reconfig(name,metainfo_url);
|
|
Packit |
8f70b4 |
if(listener)
|
|
Packit |
8f70b4 |
StartDHT();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const xstring& Torrent::Status()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(metainfo_copy)
|
|
Packit |
8f70b4 |
return xstring::format(_("Getting meta-data: %s"),metainfo_copy->GetStatus());
|
|
Packit |
8f70b4 |
if(validating) {
|
|
Packit |
8f70b4 |
return xstring::format(_("Validation: %u/%u (%u%%) %s%s"),validate_index,total_pieces,
|
|
Packit |
8f70b4 |
validate_index*100/total_pieces,recv_rate.GetStrS(),
|
|
Packit |
8f70b4 |
recv_rate.GetETAStrFromSize((off_t)(total_pieces-validate_index-1)*piece_length+last_piece_length).get());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(building)
|
|
Packit |
8f70b4 |
return building->Status();
|
|
Packit |
8f70b4 |
if(!metadata && !build_md) {
|
|
Packit |
8f70b4 |
if(md_download.length()>0)
|
|
Packit |
8f70b4 |
return xstring::format(_("Getting meta-data: %s"),
|
|
Packit |
8f70b4 |
xstring::format("%u/%u",(unsigned)md_download.length(),(unsigned)metadata_size).get());
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
return xstring::get_tmp(_("Waiting for meta-data..."));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(shutting_down) {
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(!trackers[i]->IsActive())
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
const char *status=trackers[i]->Status();
|
|
Packit |
8f70b4 |
if(status[0]) {
|
|
Packit |
8f70b4 |
xstring &s=xstring::get_tmp(_("Shutting down: "));
|
|
Packit |
8f70b4 |
if(trackers.count()>1)
|
|
Packit |
8f70b4 |
s.appendf("%d. ",i+1);
|
|
Packit |
8f70b4 |
s.append(status);
|
|
Packit |
8f70b4 |
return s;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return xstring::get_tmp("");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(total_length==0)
|
|
Packit |
8f70b4 |
return xstring::get_tmp("");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
char dn_buf[LONGEST_HUMAN_READABLE + 1];
|
|
Packit |
8f70b4 |
char up_buf[LONGEST_HUMAN_READABLE + 1];
|
|
Packit |
8f70b4 |
xstring& buf=xstring::format("dn:%s %sup:%s %s",
|
|
Packit |
8f70b4 |
human_readable(total_recv, dn_buf, human_autoscale|human_SI, 1, 1),
|
|
Packit |
8f70b4 |
recv_rate.GetStrS(),
|
|
Packit |
8f70b4 |
human_readable(total_sent, up_buf, human_autoscale|human_SI, 1, 1),
|
|
Packit |
8f70b4 |
send_rate.GetStrS());
|
|
Packit |
8f70b4 |
if(!complete) {
|
|
Packit |
8f70b4 |
buf.appendf("complete:%u/%u (%u%%)",complete_pieces,total_pieces,
|
|
Packit |
8f70b4 |
unsigned((total_length-total_left)*100/total_length));
|
|
Packit |
8f70b4 |
buf.append(' ');
|
|
Packit |
8f70b4 |
if(min_piece_sources)
|
|
Packit |
8f70b4 |
buf.append(recv_rate.GetETAStrFromSize(total_left));
|
|
Packit |
8f70b4 |
if(end_game)
|
|
Packit |
8f70b4 |
buf.append(" end-game");
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
buf.appendf("complete, ratio:%.2f/%.2f/%.2f",
|
|
Packit |
8f70b4 |
GetMinPerPieceRatio(),GetRatio(),GetMaxPerPieceRatio());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return buf;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
TorrentPeer::TorrentPeer(Torrent *p,const sockaddr_u *a,int t_no)
|
|
Packit |
8f70b4 |
: timeout_timer(360), retry_timer(30), keepalive_timer(120),
|
|
Packit |
8f70b4 |
choke_timer(10), interest_timer(10), activity_timer(300),
|
|
Packit |
8f70b4 |
msg_ext_metadata(0), msg_ext_pex(0), metadata_size(0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
parent=p;
|
|
Packit |
8f70b4 |
tracker_no=t_no;
|
|
Packit |
8f70b4 |
addr=*a;
|
|
Packit |
8f70b4 |
sock=-1;
|
|
Packit |
8f70b4 |
udp_port=0;
|
|
Packit |
8f70b4 |
connected=false;
|
|
Packit |
8f70b4 |
passive=false;
|
|
Packit |
8f70b4 |
duplicate=0;
|
|
Packit |
8f70b4 |
myself=false;
|
|
Packit |
8f70b4 |
peer_choking=true;
|
|
Packit |
8f70b4 |
am_choking=true;
|
|
Packit |
8f70b4 |
peer_interested=false;
|
|
Packit |
8f70b4 |
am_interested=false;
|
|
Packit |
8f70b4 |
upload_only=false;
|
|
Packit |
8f70b4 |
peer_complete_pieces=0;
|
|
Packit |
8f70b4 |
retry_timer.Stop();
|
|
Packit |
8f70b4 |
retry_timer.AddRandom(2);
|
|
Packit |
8f70b4 |
choke_timer.Stop();
|
|
Packit |
8f70b4 |
interest_timer.Stop();
|
|
Packit |
8f70b4 |
last_piece=NO_PIECE;
|
|
Packit |
8f70b4 |
if(addr.is_reserved() || addr.is_multicast() || addr.port()==0)
|
|
Packit |
8f70b4 |
SetError("invalid peer address");
|
|
Packit |
8f70b4 |
peer_bytes_pool[0]=peer_bytes_pool[1]=0;
|
|
Packit |
8f70b4 |
peer_recv=peer_sent=0;
|
|
Packit |
8f70b4 |
invalid_piece_count=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
TorrentPeer::~TorrentPeer()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::PrepareToDie()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::Connect(int s,IOBuffer *rb)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
sock=s;
|
|
Packit |
8f70b4 |
recv_buf=rb;
|
|
Packit |
8f70b4 |
connected=true;
|
|
Packit |
8f70b4 |
passive=true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SetError(const char *s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
error=Error::Fatal(s);
|
|
Packit |
8f70b4 |
LogError(11,"fatal error: %s",s);
|
|
Packit |
8f70b4 |
Disconnect(s);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::Restart()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!Connected())
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
retry_timer.Stop();
|
|
Packit |
8f70b4 |
retry_timer.AddRandom(2);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::Disconnect(const char *dc)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
if(Connected() && !recv_buf->Eof())
|
|
Packit |
8f70b4 |
LogNote(4,"closing connection");
|
|
Packit |
8f70b4 |
recv_queue.empty();
|
|
Packit |
8f70b4 |
ClearSentQueue();
|
|
Packit |
8f70b4 |
if(peer_bitfield) {
|
|
Packit |
8f70b4 |
for(unsigned p=0; p<parent->total_pieces; p++)
|
|
Packit |
8f70b4 |
SetPieceHaving(p,false);
|
|
Packit |
8f70b4 |
peer_bitfield=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
peer_id.unset();
|
|
Packit |
8f70b4 |
fast_set.empty();
|
|
Packit |
8f70b4 |
suggested_set.empty();
|
|
Packit |
8f70b4 |
recv_buf=0;
|
|
Packit |
8f70b4 |
send_buf=0;
|
|
Packit |
8f70b4 |
if(sock!=-1) {
|
|
Packit |
8f70b4 |
close(sock);
|
|
Packit |
8f70b4 |
sock=-1;
|
|
Packit |
8f70b4 |
connected=false;
|
|
Packit |
8f70b4 |
last_dc.set(dc);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
parent->am_interested_peers_count-=am_interested;
|
|
Packit |
8f70b4 |
am_interested=false;
|
|
Packit |
8f70b4 |
parent->am_not_choking_peers_count-=!am_choking;
|
|
Packit |
8f70b4 |
am_choking=true;
|
|
Packit |
8f70b4 |
peer_interested=false;
|
|
Packit |
8f70b4 |
peer_choking=true;
|
|
Packit |
8f70b4 |
peer_complete_pieces=0;
|
|
Packit |
8f70b4 |
retry_timer.Reset();
|
|
Packit |
8f70b4 |
choke_timer.Stop();
|
|
Packit |
8f70b4 |
interest_timer.Stop();
|
|
Packit |
8f70b4 |
// return to main pool
|
|
Packit |
8f70b4 |
parent->PeerBytesUsed(-peer_bytes_pool[RateLimit::GET],RateLimit::GET);
|
|
Packit |
8f70b4 |
parent->PeerBytesUsed(-peer_bytes_pool[RateLimit::PUT],RateLimit::PUT);
|
|
Packit |
8f70b4 |
peer_bytes_pool[0]=peer_bytes_pool[1]=0;
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SendHandshake()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *const protocol="BitTorrent protocol";
|
|
Packit |
8f70b4 |
int proto_len=strlen(protocol);
|
|
Packit |
8f70b4 |
send_buf->PackUINT8(proto_len);
|
|
Packit |
8f70b4 |
send_buf->Put(protocol,proto_len);
|
|
Packit |
8f70b4 |
static char extensions[8] = {
|
|
Packit |
8f70b4 |
// extensions[7]&0x01 - DHT Protocol (http://www.bittorrent.org/beps/bep_0005.html)
|
|
Packit |
8f70b4 |
// extensions[7]&0x04 - Fast Extension (http://www.bittorrent.org/beps/bep_0006.html)
|
|
Packit |
8f70b4 |
// extensions[5]&0x10 - Extension Protocol (http://www.bittorrent.org/beps/bep_0010.html)
|
|
Packit |
8f70b4 |
0, 0, 0, 0, 0, 0x10, 0, 0x05,
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
if(ResMgr::QueryBool("torrent:use-dht",0))
|
|
Packit |
8f70b4 |
extensions[7]|=0x01;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
extensions[7]&=~0x01;
|
|
Packit |
8f70b4 |
send_buf->Put(extensions,8);
|
|
Packit |
8f70b4 |
send_buf->Put(parent->info_hash);
|
|
Packit |
8f70b4 |
send_buf->Put(parent->my_peer_id);
|
|
Packit |
8f70b4 |
LogSend(9,"handshake");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
TorrentPeer::unpack_status_t TorrentPeer::RecvHandshake()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unsigned proto_len=0;
|
|
Packit |
8f70b4 |
if(recv_buf->Size()>0)
|
|
Packit |
8f70b4 |
proto_len=recv_buf->UnpackUINT8();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if((unsigned)recv_buf->Size()<1+proto_len+8+SHA1_DIGEST_SIZE+Torrent::PEER_ID_LEN)
|
|
Packit |
8f70b4 |
return recv_buf->Eof() ? UNPACK_PREMATURE_EOF : UNPACK_NO_DATA_YET;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int unpacked=1;
|
|
Packit |
8f70b4 |
const char *data=recv_buf->Get();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring protocol(data+unpacked,proto_len);
|
|
Packit |
8f70b4 |
unpacked+=proto_len;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
memcpy(extensions,data+unpacked,8);
|
|
Packit |
8f70b4 |
unpacked+=8; // 8 bytes are reserved (extensions)
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
xstring peer_info_hash(data+unpacked,SHA1_DIGEST_SIZE);
|
|
Packit |
8f70b4 |
unpacked+=SHA1_DIGEST_SIZE;
|
|
Packit |
8f70b4 |
if(peer_info_hash.ne(parent->info_hash)) {
|
|
Packit |
8f70b4 |
LogError(0,"got info_hash: %s, wanted: %s",peer_info_hash.hexdump(),parent->info_hash.hexdump());
|
|
Packit |
8f70b4 |
SetError("peer info_hash mismatch");
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const xstring& tmp_peer_id=xstring::get_tmp(recv_buf->Get()+unpacked,Torrent::PEER_ID_LEN);
|
|
Packit |
8f70b4 |
unpacked+=Torrent::PEER_ID_LEN;
|
|
Packit |
8f70b4 |
// if we have already such a peer, then this peer or the other one
|
|
Packit |
8f70b4 |
// much be marked as duplicate and then removed in ScanPeers.
|
|
Packit |
8f70b4 |
duplicate=parent->FindPeerById(tmp_peer_id);
|
|
Packit |
8f70b4 |
if(duplicate && !duplicate->Connected()) {
|
|
Packit |
8f70b4 |
duplicate->duplicate=this;
|
|
Packit |
8f70b4 |
duplicate=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
peer_id.set(tmp_peer_id);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
recv_buf->Skip(unpacked);
|
|
Packit |
8f70b4 |
LogRecv(4,xstring::format("handshake, %s, peer_id: %s, reserved: %02x%02x%02x%02x%02x%02x%02x%02x",
|
|
Packit |
8f70b4 |
protocol.dump(),url::encode(peer_id,"").get(),
|
|
Packit |
8f70b4 |
extensions[0],extensions[1],extensions[2],extensions[3],
|
|
Packit |
8f70b4 |
extensions[4],extensions[5],extensions[6],extensions[7]));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::SendExtensions()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!LTEPExtensionEnabled())
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
xmap_p<BeNode> m;
|
|
Packit |
8f70b4 |
m.add("ut_metadata",new BeNode(MSG_EXT_METADATA));
|
|
Packit |
8f70b4 |
m.add("ut_pex",new BeNode(MSG_EXT_PEX));
|
|
Packit |
8f70b4 |
xmap_p<BeNode> ext;
|
|
Packit |
8f70b4 |
ext.add("m",new BeNode(&m);;
|
|
Packit |
8f70b4 |
ext.add("p",new BeNode(parent->GetPort()));
|
|
Packit |
8f70b4 |
ext.add("v",new BeNode(PACKAGE "/" VERSION));
|
|
Packit |
8f70b4 |
ext.add("reqq",new BeNode(MAX_QUEUE_LEN*16));
|
|
Packit |
8f70b4 |
if(parent->Complete())
|
|
Packit |
8f70b4 |
ext.add("upload_only",new BeNode(1));
|
|
Packit |
8f70b4 |
if(parent->metadata)
|
|
Packit |
8f70b4 |
ext.add("metadata_size",new BeNode(parent->metadata.length()));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *ip=ResMgr::Query("torrent:ip",0);
|
|
Packit |
8f70b4 |
sockaddr_u sa;
|
|
Packit |
8f70b4 |
socklen_t sa_len=sizeof(sa);
|
|
Packit |
8f70b4 |
if((ip && ip[0] && inet_aton(ip,&sa.in.sin_addr))
|
|
Packit |
8f70b4 |
|| (getsockname(sock,&sa.sa,&sa_len)!=-1 && sa.sa.sa_family==AF_INET))
|
|
Packit |
8f70b4 |
ext.add("ipv4",new BeNode((const char*)&sa.in.sin_addr,4));
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
const char *ipv6=ResMgr::Query("torrent:ipv6",0);
|
|
Packit |
8f70b4 |
sa_len=sizeof(sa);
|
|
Packit |
8f70b4 |
if((ipv6 && ipv6[0] && inet_pton(AF_INET6,ipv6,&sa.in6.sin6_addr)>0)
|
|
Packit |
8f70b4 |
|| (getsockname(sock,&sa.sa,&sa_len)!=-1 && sa.sa.sa_family==AF_INET6))
|
|
Packit |
8f70b4 |
ext.add("ipv6",new BeNode((const char*)&sa.in6.sin6_addr,16));
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
sa_len=sizeof(sa);
|
|
Packit |
8f70b4 |
if(getpeername(sock,&sa.sa,&sa_len)!=-1) {
|
|
Packit |
8f70b4 |
if(sa.sa.sa_family==AF_INET)
|
|
Packit |
8f70b4 |
ext.add("yourip",new BeNode((const char*)&sa.in.sin_addr,4));
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
else if(sa.sa.sa_family==AF_INET6)
|
|
Packit |
8f70b4 |
ext.add("yourip",new BeNode((const char*)&sa.in6.sin6_addr,16));
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
PacketExtended pkt(MSG_EXT_HANDSHAKE,new BeNode(&ext));
|
|
Packit |
8f70b4 |
pkt.Pack(send_buf);
|
|
Packit |
8f70b4 |
LogSend(9,xstring::format("extended(%u,%s)",pkt.code,pkt.data->Format1()));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SendDataReply()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const PacketRequest *p=recv_queue.next();
|
|
Packit |
8f70b4 |
Enter(parent);
|
|
Packit |
8f70b4 |
const xstring& data=parent->RetrieveBlock(p->index,p->begin,p->req_length);
|
|
Packit |
8f70b4 |
Leave(parent);
|
|
Packit |
8f70b4 |
if(!Connected()) // we can be disconnected by parent
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(data.length()!=p->req_length) {
|
|
Packit |
8f70b4 |
if(parent->my_bitfield->get_bit(p->index))
|
|
Packit |
8f70b4 |
parent->SetError(xstring::format("failed to read piece %u",p->index));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogSend(8,xstring::format("piece:%u begin:%u size:%u",p->index,p->begin,p->req_length));
|
|
Packit |
8f70b4 |
PacketPiece(p->index,p->begin,data).Pack(send_buf);
|
|
Packit |
8f70b4 |
peer_sent+=data.length();
|
|
Packit |
8f70b4 |
peer_send_rate.Add(data.length());
|
|
Packit |
8f70b4 |
parent->AccountSend(p->index,data.length());
|
|
Packit |
8f70b4 |
BytesPut(data.length());
|
|
Packit |
8f70b4 |
activity_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int TorrentPeer::SendDataRequests(unsigned p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(p==NO_PIECE)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
if(parent->my_bitfield->get_bit(p)
|
|
Packit |
8f70b4 |
|| !peer_bitfield || !peer_bitfield->get_bit(p))
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int sent=0;
|
|
Packit |
8f70b4 |
unsigned blocks=parent->BlocksInPiece(p);
|
|
Packit |
8f70b4 |
unsigned bytes_allowed=BytesAllowed(RateLimit::GET);
|
|
Packit |
8f70b4 |
for(unsigned b=0; b
|
|
Packit |
8f70b4 |
if(parent->BlockPresent(p,b))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(parent->piece_info[p].downloader_for(b)) {
|
|
Packit |
8f70b4 |
if(!parent->end_game)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(parent->piece_info[p].downloader_for(b)==this)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(FindRequest(p,b*Torrent::BLOCK_SIZE)>=0)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
unsigned begin=b*Torrent::BLOCK_SIZE;
|
|
Packit |
8f70b4 |
unsigned len=Torrent::BLOCK_SIZE;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(b==blocks-1) {
|
|
Packit |
8f70b4 |
assert(begin<parent->PieceLength(p));
|
|
Packit |
8f70b4 |
unsigned max_len=parent->PieceLength(p)-begin;
|
|
Packit |
8f70b4 |
if(len>max_len)
|
|
Packit |
8f70b4 |
len=max_len;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(bytes_allowed
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
parent->SetDownloader(p,b,0,this);
|
|
Packit |
8f70b4 |
PacketRequest *req=new PacketRequest(p,b*Torrent::BLOCK_SIZE,len);
|
|
Packit |
8f70b4 |
LogSend(6,xstring::format("request piece:%u begin:%u size:%u",p,b*Torrent::BLOCK_SIZE,len));
|
|
Packit |
8f70b4 |
req->Pack(send_buf);
|
|
Packit |
8f70b4 |
sent_queue.push(req);
|
|
Packit |
8f70b4 |
SetLastPiece(p);
|
|
Packit |
8f70b4 |
sent++;
|
|
Packit |
8f70b4 |
activity_timer.Reset();
|
|
Packit |
8f70b4 |
bytes_allowed-=len;
|
|
Packit |
8f70b4 |
BytesGot(len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(sent_queue.count()>=MAX_QUEUE_LEN)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return sent;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool TorrentPeer::InFastSet(unsigned p) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(fast_set[i]==p)
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SendDataRequests()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
assert(am_interested);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(peer_choking && !FastExtensionEnabled())
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(sent_queue.count()>=MAX_QUEUE_LEN)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(!BytesAllowedToGet(Torrent::BLOCK_SIZE))
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(peer_choking) {
|
|
Packit |
8f70b4 |
// try to continue getting last piece if it is in the fast set
|
|
Packit |
8f70b4 |
unsigned last_piece=GetLastPiece();
|
|
Packit |
8f70b4 |
if(last_piece!=NO_PIECE && InFastSet(last_piece) && SendDataRequests(last_piece)>0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
// try fast set when choking
|
|
Packit |
8f70b4 |
while(fast_set.count()>0) {
|
|
Packit |
8f70b4 |
unsigned p=fast_set[0];
|
|
Packit |
8f70b4 |
if(SendDataRequests(p)>0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
fast_set.next();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// try to continue getting last piece
|
|
Packit |
8f70b4 |
if(SendDataRequests(GetLastPiece())>0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// try suggested pieces
|
|
Packit |
8f70b4 |
while(suggested_set.count()>0) {
|
|
Packit |
8f70b4 |
unsigned p=suggested_set.next();
|
|
Packit |
8f70b4 |
if(SendDataRequests(p)>0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// pick a new piece
|
|
Packit |
8f70b4 |
unsigned p=NO_PIECE;
|
|
Packit |
8f70b4 |
for(int i=0; i<parent->pieces_needed.count(); i++) {
|
|
Packit |
8f70b4 |
if(peer_bitfield->get_bit(parent->pieces_needed[i])) {
|
|
Packit |
8f70b4 |
p=parent->pieces_needed[i];
|
|
Packit |
8f70b4 |
if(parent->my_bitfield->get_bit(p))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
// add some randomness, so that different instances don't synchronize
|
|
Packit |
8f70b4 |
if(parent->AllBlocksAbsent(p) && random()/13%16==0)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(SendDataRequests(p)>0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(p==NO_PIECE && interest_timer.Stopped())
|
|
Packit |
8f70b4 |
SetAmInterested(false);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::Have(unsigned p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!send_buf)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
LogSend(9,xstring::format("have(%u)",p));
|
|
Packit |
8f70b4 |
PacketHave(p).Pack(send_buf);
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int TorrentPeer::FindRequest(unsigned piece,unsigned begin) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
const PacketRequest *req=sent_queue[i];
|
|
Packit |
8f70b4 |
if(req->index==piece && req->begin==begin)
|
|
Packit |
8f70b4 |
return i;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return -1;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::CancelBlock(unsigned p,unsigned b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!send_buf)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
int i=FindRequest(p,b);
|
|
Packit |
8f70b4 |
if(i>=0) {
|
|
Packit |
8f70b4 |
const PacketRequest *req=sent_queue[i];
|
|
Packit |
8f70b4 |
LogSend(9,xstring::format("cancel(%u,%u)",p,b));
|
|
Packit |
8f70b4 |
PacketCancel(p,b,req->req_length).Pack(send_buf);
|
|
Packit |
8f70b4 |
parent->SetDownloader(p,b/Torrent::BLOCK_SIZE,this,0);
|
|
Packit |
8f70b4 |
sent_queue.remove(i);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// mark that peer as having an invalid piece
|
|
Packit |
8f70b4 |
void TorrentPeer::MarkPieceInvalid(unsigned p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
invalid_piece_count++;
|
|
Packit |
8f70b4 |
SetPieceHaving(p,false);
|
|
Packit |
8f70b4 |
SetAmInterested(am_interested);
|
|
Packit |
8f70b4 |
if(invalid_piece_count>5)
|
|
Packit |
8f70b4 |
parent->BlackListPeer(this,"1d");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::ClearSentQueue(int i)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(i<0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
// without Fast Extension we assume sequential packet processing,
|
|
Packit |
8f70b4 |
// thus clear also all sent requests before this one.
|
|
Packit |
8f70b4 |
while(i-->=0) {
|
|
Packit |
8f70b4 |
const PacketRequest *req=sent_queue.next();
|
|
Packit |
8f70b4 |
parent->PeerBytesGot(-req->req_length);
|
|
Packit |
8f70b4 |
parent->SetDownloader(req->index,req->begin/Torrent::BLOCK_SIZE,this,0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
const PacketRequest *req=sent_queue[i];
|
|
Packit |
8f70b4 |
parent->PeerBytesGot(-req->req_length);
|
|
Packit |
8f70b4 |
parent->SetDownloader(req->index,req->begin/Torrent::BLOCK_SIZE,this,0);
|
|
Packit |
8f70b4 |
sent_queue.remove(i);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int TorrentPeer::BytesAllowed(RateLimit::dir_t dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int a=parent->PeerBytesAllowed(this,dir);
|
|
Packit |
8f70b4 |
int pool_target=Torrent::BLOCK_SIZE*2;
|
|
Packit |
8f70b4 |
if(peer_bytes_pool[dir]
|
|
Packit |
8f70b4 |
int to_pool=pool_target-peer_bytes_pool[dir];
|
|
Packit |
8f70b4 |
if(to_pool>a)
|
|
Packit |
8f70b4 |
to_pool=a;
|
|
Packit |
8f70b4 |
peer_bytes_pool[dir]+=to_pool;
|
|
Packit |
8f70b4 |
a-=to_pool;
|
|
Packit |
8f70b4 |
parent->PeerBytesUsed(to_pool,dir);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return peer_bytes_pool[dir]+a;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
bool TorrentPeer::BytesAllowed(RateLimit::dir_t dir,unsigned bytes)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int a=BytesAllowed(dir);
|
|
Packit |
8f70b4 |
if(bytes<=(unsigned)a)
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
TimeoutS(1);
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::BytesUsed(int b,RateLimit::dir_t dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(peer_bytes_pool[dir]>=b)
|
|
Packit |
8f70b4 |
peer_bytes_pool[dir]-=b;
|
|
Packit |
8f70b4 |
else {
|
|
Packit |
8f70b4 |
b-=peer_bytes_pool[dir];
|
|
Packit |
8f70b4 |
peer_bytes_pool[dir]=0;
|
|
Packit |
8f70b4 |
parent->PeerBytesUsed(b,dir);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
unsigned TorrentPeer::GetLastPiece() const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!peer_bitfield)
|
|
Packit |
8f70b4 |
return NO_PIECE;
|
|
Packit |
8f70b4 |
unsigned p=last_piece;
|
|
Packit |
8f70b4 |
// continue if have any blocks already
|
|
Packit |
8f70b4 |
if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
|
|
Packit |
8f70b4 |
&& parent->AnyBlocksPresent(p)
|
|
Packit |
8f70b4 |
&& peer_bitfield->get_bit(p))
|
|
Packit |
8f70b4 |
return p;
|
|
Packit |
8f70b4 |
p=parent->last_piece;
|
|
Packit |
8f70b4 |
if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
|
|
Packit |
8f70b4 |
&& peer_bitfield->get_bit(p))
|
|
Packit |
8f70b4 |
return p;
|
|
Packit |
8f70b4 |
p=last_piece;
|
|
Packit |
8f70b4 |
if(p!=NO_PIECE && !parent->my_bitfield->get_bit(p)
|
|
Packit |
8f70b4 |
&& peer_bitfield->get_bit(p))
|
|
Packit |
8f70b4 |
return p;
|
|
Packit |
8f70b4 |
return NO_PIECE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SetLastPiece(unsigned p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(last_piece==NO_PIECE || parent->my_bitfield->get_bit(last_piece))
|
|
Packit |
8f70b4 |
last_piece=p;
|
|
Packit |
8f70b4 |
if(parent->last_piece==NO_PIECE || parent->my_bitfield->get_bit(parent->last_piece))
|
|
Packit |
8f70b4 |
parent->last_piece=p;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SetAmInterested(bool interest)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(invalid_piece_count>5)
|
|
Packit |
8f70b4 |
interest=false;
|
|
Packit |
8f70b4 |
if(am_interested==interest)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
LogSend(6,interest?"interested":"uninterested");
|
|
Packit |
8f70b4 |
Packet(interest?MSG_INTERESTED:MSG_UNINTERESTED).Pack(send_buf);
|
|
Packit |
8f70b4 |
parent->am_interested_peers_count+=(interest-am_interested);
|
|
Packit |
8f70b4 |
am_interested=interest;
|
|
Packit |
8f70b4 |
interest_timer.Reset();
|
|
Packit |
8f70b4 |
if(am_interested)
|
|
Packit |
8f70b4 |
parent->am_interested_timer.Reset();
|
|
Packit |
8f70b4 |
(void)BytesAllowed(RateLimit::GET); // draw some bytes from the common pool
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::SetAmChoking(bool c)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(am_choking==c)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
Enter();
|
|
Packit |
8f70b4 |
LogSend(6,c?"choke":"unchoke");
|
|
Packit |
8f70b4 |
Packet(c?MSG_CHOKE:MSG_UNCHOKE).Pack(send_buf);
|
|
Packit |
8f70b4 |
parent->am_not_choking_peers_count-=(c-am_choking);
|
|
Packit |
8f70b4 |
am_choking=c;
|
|
Packit |
8f70b4 |
choke_timer.Reset();
|
|
Packit |
8f70b4 |
if(am_choking) {
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
recv_queue.empty();
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
// send rejects
|
|
Packit |
8f70b4 |
while(recv_queue.count()>0) {
|
|
Packit |
8f70b4 |
const PacketRequest *p=recv_queue.next();
|
|
Packit |
8f70b4 |
LogSend(6,xstring::format("reject-request piece:%u begin:%u size:%u",p->index,p->begin,p->req_length));
|
|
Packit |
8f70b4 |
PacketRejectRequest(p->index,p->begin,p->req_length).Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Leave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SetPieceHaving(unsigned p,bool have)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int diff = (have - peer_bitfield->get_bit(p));
|
|
Packit |
8f70b4 |
if(!diff)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
parent->piece_info[p].add_sources_count(diff);
|
|
Packit |
8f70b4 |
peer_complete_pieces+=diff;
|
|
Packit |
8f70b4 |
peer_bitfield->set_bit(p,have);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(parent->piece_info[p].get_sources_count()==0)
|
|
Packit |
8f70b4 |
parent->SetPieceNotWanted(p);
|
|
Packit |
8f70b4 |
if(have && send_buf && !am_interested && !parent->my_bitfield->get_bit(p)
|
|
Packit |
8f70b4 |
&& parent->NeedMoreUploaders()) {
|
|
Packit |
8f70b4 |
SetAmInterested(true);
|
|
Packit |
8f70b4 |
SetLastPiece(p);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::HandlePacket(Packet *p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(p->GetPacketType())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case MSG_KEEPALIVE: {
|
|
Packit |
8f70b4 |
LogRecv(5,"keep-alive");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_CHOKE: {
|
|
Packit |
8f70b4 |
LogRecv(5,"choke");
|
|
Packit |
8f70b4 |
peer_choking=true;
|
|
Packit |
8f70b4 |
ClearSentQueue(); // discard pending requests
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_UNCHOKE: {
|
|
Packit |
8f70b4 |
LogRecv(5,"unchoke");
|
|
Packit |
8f70b4 |
peer_choking=false;
|
|
Packit |
8f70b4 |
if(am_interested)
|
|
Packit |
8f70b4 |
SendDataRequests();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_INTERESTED: {
|
|
Packit |
8f70b4 |
LogRecv(5,"interested");
|
|
Packit |
8f70b4 |
peer_interested=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_UNINTERESTED: {
|
|
Packit |
8f70b4 |
LogRecv(5,"uninterested");
|
|
Packit |
8f70b4 |
recv_queue.empty();
|
|
Packit |
8f70b4 |
peer_interested=false;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_HAVE: {
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
PacketHave *pp=static_cast<PacketHave*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("have(%u)",pp->piece));
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(pp->piece>=parent->total_pieces) {
|
|
Packit |
8f70b4 |
SetError("invalid piece index");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetPieceHaving(pp->piece,true);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_BITFIELD: {
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
PacketBitField *pp=static_cast<PacketBitField*>(p);
|
|
Packit |
8f70b4 |
if(pp->bitfield->count()<(int)parent->total_pieces/8) {
|
|
Packit |
8f70b4 |
LogError(9,"bitfield length %d, expected %u",pp->bitfield->count(),parent->total_pieces/8);
|
|
Packit |
8f70b4 |
SetError("invalid bitfield length");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->bitfield->has_any_set(parent->total_pieces,pp->bitfield->get_bit_length())) {
|
|
Packit |
8f70b4 |
SetError("bitfield has spare bits set");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
for(unsigned p=0; p<parent->total_pieces; p++)
|
|
Packit |
8f70b4 |
SetPieceHaving(p,pp->bitfield->get_bit(p));
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("bitfield(%u/%u)",peer_complete_pieces,parent->total_pieces));
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_PORT: {
|
|
Packit |
8f70b4 |
PacketPort *pp=static_cast<PacketPort*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("port(%u)",pp->port));
|
|
Packit |
8f70b4 |
udp_port=pp->port;
|
|
Packit |
8f70b4 |
if(DHT_Enabled() && Torrent::dht) {
|
|
Packit |
8f70b4 |
sockaddr_u a(addr);
|
|
Packit |
8f70b4 |
a.set_port(udp_port);
|
|
Packit |
8f70b4 |
Torrent::GetDHT(a)->SendPing(a);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_HAVE_ALL: {
|
|
Packit |
8f70b4 |
LogRecv(5,"have-all");
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
SetError("fast extension is disabled");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
for(unsigned p=0; p<parent->total_pieces; p++)
|
|
Packit |
8f70b4 |
SetPieceHaving(p,1);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_HAVE_NONE: {
|
|
Packit |
8f70b4 |
LogRecv(5,"have-none");
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
SetError("fast extension is disabled");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
for(unsigned p=0; p<parent->total_pieces; p++)
|
|
Packit |
8f70b4 |
SetPieceHaving(p,0);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_SUGGEST_PIECE: {
|
|
Packit |
8f70b4 |
PacketSuggestPiece *pp=static_cast<PacketSuggestPiece*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("suggest-piece:%u",pp->piece));
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
SetError("fast extension is disabled");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(pp->piece>=parent->total_pieces) {
|
|
Packit |
8f70b4 |
SetError("invalid piece index");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
suggested_set.push(pp->piece);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_ALLOWED_FAST: {
|
|
Packit |
8f70b4 |
PacketAllowedFast *pp=static_cast<PacketAllowedFast*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("allowed-fast:%u",pp->piece));
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
SetError("fast extension is disabled");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(pp->piece>=parent->total_pieces) {
|
|
Packit |
8f70b4 |
SetError("invalid piece index");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
fast_set.push(pp->piece);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_REJECT_REQUEST: {
|
|
Packit |
8f70b4 |
PacketRejectRequest *pp=static_cast<PacketRejectRequest*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("reject-request(%u,%u)",pp->index,pp->begin));
|
|
Packit |
8f70b4 |
if(!FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
SetError("fast extension is disabled");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int i=FindRequest(pp->index,pp->begin);
|
|
Packit |
8f70b4 |
if(i>=0)
|
|
Packit |
8f70b4 |
ClearSentQueue(i);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_EXTENDED: {
|
|
Packit |
8f70b4 |
PacketExtended *pp=static_cast<PacketExtended*>(p);
|
|
Packit |
8f70b4 |
LogRecv(9,xstring::format("extended(%u,%s)",pp->code,pp->data->Format1()));
|
|
Packit |
8f70b4 |
HandleExtendedMessage(pp);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_PIECE: {
|
|
Packit |
8f70b4 |
PacketPiece *pp=static_cast<PacketPiece*>(p);
|
|
Packit |
8f70b4 |
LogRecv(7,xstring::format("piece:%u begin:%u size:%u",pp->index,pp->begin,(unsigned)pp->data.length()));
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(pp->index>=parent->total_pieces) {
|
|
Packit |
8f70b4 |
SetError("invalid piece index");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->begin>=parent->PieceLength(pp->index)) {
|
|
Packit |
8f70b4 |
SetError("invalid data offset");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->begin+pp->data.length() > parent->PieceLength(pp->index)) {
|
|
Packit |
8f70b4 |
SetError("invalid data length");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int i=FindRequest(pp->index,pp->begin);
|
|
Packit |
8f70b4 |
if(i<0) {
|
|
Packit |
8f70b4 |
// SetError("got a piece that was not requested");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ClearSentQueue(i);
|
|
Packit |
8f70b4 |
parent->PeerBytesGot(pp->data.length()); // re-take the bytes returned by ClearSentQueue
|
|
Packit |
8f70b4 |
Enter(parent);
|
|
Packit |
8f70b4 |
parent->StoreBlock(pp->index,pp->begin,pp->data.length(),pp->data.get(),this);
|
|
Packit |
8f70b4 |
Leave(parent);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int len=pp->data.length();
|
|
Packit |
8f70b4 |
peer_recv+=len;
|
|
Packit |
8f70b4 |
peer_recv_rate.Add(len);
|
|
Packit |
8f70b4 |
parent->AccountRecv(pp->index,len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// request another block from the same piece
|
|
Packit |
8f70b4 |
if(am_interested && (!peer_choking || InFastSet(pp->index)))
|
|
Packit |
8f70b4 |
SendDataRequests(pp->index);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_REQUEST: {
|
|
Packit |
8f70b4 |
PacketRequest *pp=static_cast<PacketRequest*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("request for piece:%u begin:%u size:%u",pp->index,pp->begin,pp->req_length));
|
|
Packit |
8f70b4 |
if(pp->req_length>Torrent::BLOCK_SIZE*2) {
|
|
Packit |
8f70b4 |
SetError("too large request");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata())
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(am_choking)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(pp->index>=parent->total_pieces) {
|
|
Packit |
8f70b4 |
SetError("invalid piece index");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->begin>=parent->PieceLength(pp->index)) {
|
|
Packit |
8f70b4 |
SetError("invalid data offset");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->begin+pp->req_length > parent->PieceLength(pp->index)) {
|
|
Packit |
8f70b4 |
SetError("invalid data length");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(recv_queue.count()>=MAX_QUEUE_LEN*16) {
|
|
Packit |
8f70b4 |
SetError("too many requests");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
recv_queue.push(pp);
|
|
Packit |
8f70b4 |
activity_timer.Reset();
|
|
Packit |
8f70b4 |
p=0;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MSG_CANCEL: {
|
|
Packit |
8f70b4 |
PacketCancel *pp=static_cast<PacketCancel*>(p);
|
|
Packit |
8f70b4 |
LogRecv(5,xstring::format("cancel(%u,%u)",pp->index,pp->begin));
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
const PacketRequest *req=recv_queue[i];
|
|
Packit |
8f70b4 |
if(req->index==pp->index && req->begin==pp->begin) {
|
|
Packit |
8f70b4 |
recv_queue.remove(i);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
delete p;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void Torrent::MetadataDownloaded()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
xstring new_info_hash;
|
|
Packit |
8f70b4 |
SHA1(md_download,new_info_hash);
|
|
Packit |
8f70b4 |
if(info_hash && info_hash.ne(new_info_hash)) {
|
|
Packit |
8f70b4 |
LogError(1,"downloaded metadata does not match info_hash, retrying");
|
|
Packit |
8f70b4 |
StartMetadataDownload();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(SetMetadata(md_download))
|
|
Packit |
8f70b4 |
Startup();
|
|
Packit |
8f70b4 |
md_download.unset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::SendMetadataRequest()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!msg_ext_metadata || !parent->md_download || parent->md_download.length()>=metadata_size
|
|
Packit |
8f70b4 |
|| parent->md_download.length()%Torrent::BLOCK_SIZE)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
xmap_p<BeNode> req;
|
|
Packit |
8f70b4 |
req.add("msg_type",new BeNode(UT_METADATA_REQUEST));
|
|
Packit |
8f70b4 |
req.add("piece",new BeNode(parent->md_download.length()/Torrent::BLOCK_SIZE));
|
|
Packit |
8f70b4 |
PacketExtended pkt(msg_ext_metadata,new BeNode(&req));
|
|
Packit |
8f70b4 |
LogSend(4,xstring::format("ut_metadata request %s",pkt.data->Format1()));
|
|
Packit |
8f70b4 |
pkt.Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void TorrentPeer::HandleExtendedMessage(PacketExtended *pp)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(pp->data->type!=BeNode::BE_DICT) {
|
|
Packit |
8f70b4 |
SetError("extended type must be DICT");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(pp->code==MSG_EXT_HANDSHAKE) {
|
|
Packit |
8f70b4 |
BeNode *m=pp->data->lookup("m",BeNode::BE_DICT);
|
|
Packit |
8f70b4 |
if(m) {
|
|
Packit |
8f70b4 |
msg_ext_metadata=m->lookup_int("ut_metadata");
|
|
Packit |
8f70b4 |
msg_ext_pex=m->lookup_int("ut_pex");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
metadata_size=parent->metadata_size=pp->data->lookup_int("metadata_size");
|
|
Packit |
8f70b4 |
upload_only=pp->data->lookup_int("upload_only");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata() && !msg_ext_metadata) {
|
|
Packit |
8f70b4 |
Disconnect("peer cannot provide metadata");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const xstring& v=pp->data->lookup_str("v");
|
|
Packit |
8f70b4 |
if(v)
|
|
Packit |
8f70b4 |
LogNote(3,"peer version is %s",v.get());
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const xstring& myip=pp->data->lookup_str("yourip");
|
|
Packit |
8f70b4 |
if(myip && myip.length()==4) {
|
|
Packit |
8f70b4 |
char ip[16];
|
|
Packit |
8f70b4 |
inet_ntop(AF_INET,myip.get(),ip,sizeof(ip));
|
|
Packit |
8f70b4 |
LogNote(5,"my external IPv4 is %s",ip);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(passive) {
|
|
Packit |
8f70b4 |
// use specified port number to connect back
|
|
Packit |
8f70b4 |
int p=pp->data->lookup_int("p");
|
|
Packit |
8f70b4 |
if(p && p>=1024 && p<=65535) {
|
|
Packit |
8f70b4 |
LogNote(9,"using port %d to connect back",p);
|
|
Packit |
8f70b4 |
addr.set_port(p);
|
|
Packit |
8f70b4 |
passive=false;
|
|
Packit |
8f70b4 |
// check the black list
|
|
Packit |
8f70b4 |
if(Torrent::BlackListed(this)) {
|
|
Packit |
8f70b4 |
SetError("blacklisted");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
// check for duplicates
|
|
Packit |
8f70b4 |
TaskRefArray<TorrentPeer>& peers=parent->peers;
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(peers[i]!=this && peers[i]->AddressEq(this)) {
|
|
Packit |
8f70b4 |
if(!peers[i]->Connected())
|
|
Packit |
8f70b4 |
peers[i]->duplicate=this;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
duplicate=peers[i].get_non_const();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(msg_ext_metadata && parent->md_download)
|
|
Packit |
8f70b4 |
SendMetadataRequest();
|
|
Packit |
8f70b4 |
} else if(pp->code==MSG_EXT_METADATA) {
|
|
Packit |
8f70b4 |
BeNode *msg_type=pp->data->lookup("msg_type",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(!msg_type) {
|
|
Packit |
8f70b4 |
SetError("ut_metadata msg_type bad or missing");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
BeNode *piece=pp->data->lookup("piece",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(!piece || piece->num<0 || piece->num>=INT_MAX/Torrent::BLOCK_SIZE) {
|
|
Packit |
8f70b4 |
SetError("ut_metadata piece bad or missing");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
size_t offset=piece->num*Torrent::BLOCK_SIZE;
|
|
Packit |
8f70b4 |
xmap_p<BeNode> reply;
|
|
Packit |
8f70b4 |
switch(msg_type->num) {
|
|
Packit |
8f70b4 |
case UT_METADATA_REQUEST: {
|
|
Packit |
8f70b4 |
if(offset>parent->metadata.length()) {
|
|
Packit |
8f70b4 |
reply.add("msg_type",new BeNode(UT_METADATA_REJECT));
|
|
Packit |
8f70b4 |
reply.add("piece",new BeNode(piece->num));
|
|
Packit |
8f70b4 |
PacketExtended pkt(msg_ext_metadata,new BeNode(&reply));
|
|
Packit |
8f70b4 |
LogSend(4,xstring::format("ut_metadata reject %s",pkt.data->Format1()));
|
|
Packit |
8f70b4 |
pkt.Pack(send_buf);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *d=parent->metadata+offset;
|
|
Packit |
8f70b4 |
unsigned len=parent->metadata.length()-offset;
|
|
Packit |
8f70b4 |
if(len>Torrent::BLOCK_SIZE)
|
|
Packit |
8f70b4 |
len=Torrent::BLOCK_SIZE;
|
|
Packit |
8f70b4 |
reply.add("msg_type",new BeNode(UT_METADATA_DATA));
|
|
Packit |
8f70b4 |
reply.add("piece",new BeNode(piece->num));
|
|
Packit |
8f70b4 |
reply.add("total_size",new BeNode(parent->metadata.length()));
|
|
Packit |
8f70b4 |
PacketExtended pkt(msg_ext_metadata,new BeNode(&reply));
|
|
Packit |
8f70b4 |
LogSend(4,xstring::format("ut_metadata data %s",pkt.data->Format1()));
|
|
Packit |
8f70b4 |
pkt.SetAppendix(d,len);
|
|
Packit |
8f70b4 |
pkt.Pack(send_buf);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case UT_METADATA_DATA: {
|
|
Packit |
8f70b4 |
if(parent->md_download) {
|
|
Packit |
8f70b4 |
if(offset==parent->md_download.length()) {
|
|
Packit |
8f70b4 |
BeNode *b_size=pp->data->lookup("total_size",BeNode::BE_INT);
|
|
Packit |
8f70b4 |
if(b_size) {
|
|
Packit |
8f70b4 |
if(metadata_size && metadata_size!=(size_t)b_size->num) {
|
|
Packit |
8f70b4 |
SetError("metadata_size mismatch with total_size");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
metadata_size=b_size->num;
|
|
Packit |
8f70b4 |
parent->metadata_size=metadata_size;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
parent->md_download.append(pp->appendix);
|
|
Packit |
8f70b4 |
if(pp->appendix.length()
|
|
Packit |
8f70b4 |
parent->MetadataDownloaded();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SendMetadataRequest();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case UT_METADATA_REJECT:
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
default:
|
|
Packit |
8f70b4 |
SetError("ut_metadata msg_type invalid value");
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} else if(pp->code==MSG_EXT_PEX) {
|
|
Packit |
8f70b4 |
if(!pex.recv_timer.Stopped())
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
pex.recv_timer.Reset();
|
|
Packit |
8f70b4 |
BeNode *added=pp->data->lookup("added",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
BeNode *added6=pp->data->lookup("added6",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
BeNode *added_f=pp->data->lookup("added.f",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
BeNode *added6_f=pp->data->lookup("added6.f",BeNode::BE_STR);
|
|
Packit |
8f70b4 |
AddPEXPeers(added,added_f,6);
|
|
Packit |
8f70b4 |
AddPEXPeers(added6,added6_f,18);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::AddPEXPeers(BeNode *added,BeNode *added_f,int addr_size)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!added)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *data=added->str;
|
|
Packit |
8f70b4 |
unsigned n=added->str.length()/addr_size;
|
|
Packit |
8f70b4 |
if(n>50)
|
|
Packit |
8f70b4 |
n=50;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *flags=0;
|
|
Packit |
8f70b4 |
if(added_f && added_f->str.length()==n)
|
|
Packit |
8f70b4 |
flags=added_f->str;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int peers_count=0;
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
unsigned char f=(flags?flags[i]:pex.CONNECTABLE);
|
|
Packit |
8f70b4 |
if(!(f&pex.CONNECTABLE))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(parent->Complete() && (f&pex.SEED))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
sockaddr_u a;
|
|
Packit |
8f70b4 |
a.set_compact(data,addr_size);
|
|
Packit |
8f70b4 |
if(!a.is_compatible(this->addr))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
parent->AddPeer(new TorrentPeer(parent,&a,TR_PEX));
|
|
Packit |
8f70b4 |
peers_count++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(peers_count>0)
|
|
Packit |
8f70b4 |
LogNote(4,"%d %s peers added from PEX message",peers_count,addr_size==6?"ipv4":"ipv6");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void TorrentPeer::SendPEXPeers()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
pex.send_timer.Reset();
|
|
Packit |
8f70b4 |
if(!msg_ext_pex || parent->Private())
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
xmap<char> old_sent;
|
|
Packit |
8f70b4 |
old_sent.move_here(pex.sent);
|
|
Packit |
8f70b4 |
int peer_count=0;
|
|
Packit |
8f70b4 |
xstring added;
|
|
Packit |
8f70b4 |
xstring added6;
|
|
Packit |
8f70b4 |
xstring added_f;
|
|
Packit |
8f70b4 |
xstring added6_f;
|
|
Packit |
8f70b4 |
xstring dropped;
|
|
Packit |
8f70b4 |
xstring dropped6;
|
|
Packit |
8f70b4 |
int a=0,a6=0,d=0,d6=0;
|
|
Packit |
8f70b4 |
for(int i=parent->peers.count(); i>0; i--) {
|
|
Packit |
8f70b4 |
const TorrentPeer *peer=parent->peers[i-1];
|
|
Packit |
8f70b4 |
if(!peer->Connected() || peer->IsPassive() || peer->Failed()
|
|
Packit |
8f70b4 |
|| !peer->addr.is_compatible(this->addr) || peer==this || peer->myself)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
const xstring& ca=peer->addr.compact();
|
|
Packit |
8f70b4 |
if(old_sent.exists(ca)) {
|
|
Packit |
8f70b4 |
old_sent.remove(ca);
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
unsigned char f=pex.CONNECTABLE;
|
|
Packit |
8f70b4 |
if(peer->Seed())
|
|
Packit |
8f70b4 |
f|=pex.SEED;
|
|
Packit |
8f70b4 |
peer_count++;
|
|
Packit |
8f70b4 |
if(peer_count>50)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(ca.length()==6) {
|
|
Packit |
8f70b4 |
added.append(ca);
|
|
Packit |
8f70b4 |
added_f.append(f);
|
|
Packit |
8f70b4 |
a++;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
added6.append(ca);
|
|
Packit |
8f70b4 |
added6_f.append(f);
|
|
Packit |
8f70b4 |
a6++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
pex.sent.add(ca,f);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
peer_count=0;
|
|
Packit |
8f70b4 |
for(old_sent.each_begin(); !old_sent.each_finished(); old_sent.each_next())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const xstring& ca=old_sent.each_key();
|
|
Packit |
8f70b4 |
peer_count++;
|
|
Packit |
8f70b4 |
if(peer_count>50) {
|
|
Packit |
8f70b4 |
// drop it later
|
|
Packit |
8f70b4 |
pex.sent.add(ca,0);
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(ca.length()==6) {
|
|
Packit |
8f70b4 |
dropped.append(ca);
|
|
Packit |
8f70b4 |
d++;
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
dropped6.append(ca);
|
|
Packit |
8f70b4 |
d6++;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(a+a6+d+d6==0)
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
xmap_p<BeNode> req;
|
|
Packit |
8f70b4 |
if(a) {
|
|
Packit |
8f70b4 |
req.add("added",new BeNode(added));
|
|
Packit |
8f70b4 |
req.add("added.f",new BeNode(added_f));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(a6) {
|
|
Packit |
8f70b4 |
req.add("added6",new BeNode(added6));
|
|
Packit |
8f70b4 |
req.add("added6.f",new BeNode(added6_f));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(d)
|
|
Packit |
8f70b4 |
req.add("dropped",new BeNode(dropped));
|
|
Packit |
8f70b4 |
if(d6)
|
|
Packit |
8f70b4 |
req.add("dropped6",new BeNode(dropped6));
|
|
Packit |
8f70b4 |
PacketExtended pkt(msg_ext_pex,new BeNode(&req));
|
|
Packit |
8f70b4 |
LogSend(4,xstring::format("ut_pex message: added=[%d,%d], dropped=[%d,%d]",a,a6,d,d6));
|
|
Packit |
8f70b4 |
pkt.Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool TorrentPeer::HasNeededPieces()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!peer_bitfield)
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
if(GetLastPiece()!=NO_PIECE)
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
for(int i=0; i<parent->pieces_needed.count(); i++)
|
|
Packit |
8f70b4 |
if(peer_bitfield->get_bit(parent->pieces_needed[i]))
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int TorrentPeer::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(error || myself)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(sock==-1) {
|
|
Packit |
8f70b4 |
if(passive)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(!retry_timer.Stopped())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(parent->IsValidating())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
sock=SocketCreateTCP(addr.sa.sa_family,0);
|
|
Packit |
8f70b4 |
if(sock==-1)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(NonFatalError(errno))
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
SetError(xstring::format(_("cannot create socket of address family %d"),addr.sa.sa_family));
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
LogNote(4,_("Connecting to peer %s port %u"),SocketNumericAddress(&addr),SocketPort(&addr));
|
|
Packit |
8f70b4 |
connected=false;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!connected) {
|
|
Packit |
8f70b4 |
int res=SocketConnect(sock,&addr);
|
|
Packit |
8f70b4 |
if(res==-1 && errno!=EINPROGRESS && errno!=EALREADY && errno!=EISCONN)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int e=errno;
|
|
Packit |
8f70b4 |
const char *error=strerror(e);
|
|
Packit |
8f70b4 |
LogError(4,"connect(%s): %s\n",GetName(),error);
|
|
Packit |
8f70b4 |
Disconnect(error);
|
|
Packit |
8f70b4 |
if(FA::NotSerious(e) && !ActivityTimedOut())
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
SetError(error);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(res==-1 && errno!=EISCONN) {
|
|
Packit |
8f70b4 |
Block(sock,POLLOUT);
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
connected=true;
|
|
Packit |
8f70b4 |
timeout_timer.Reset();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!recv_buf) {
|
|
Packit |
8f70b4 |
recv_buf=new IOBufferFDStream(new FDStream(sock,"<input-socket>"),IOBuffer::GET);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!send_buf) {
|
|
Packit |
8f70b4 |
send_buf=new IOBufferFDStream(new FDStream(sock,"<output-socket>"),IOBuffer::PUT);
|
|
Packit |
8f70b4 |
SendHandshake();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(send_buf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,"send: %s",send_buf->ErrorText());
|
|
Packit |
8f70b4 |
Disconnect(send_buf->ErrorText());
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(recv_buf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,"receive: %s",recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
Disconnect(recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!peer_id) {
|
|
Packit |
8f70b4 |
// expect handshake
|
|
Packit |
8f70b4 |
unpack_status_t s=RecvHandshake();
|
|
Packit |
8f70b4 |
if(s==UNPACK_NO_DATA_YET)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(s!=UNPACK_SUCCESS) {
|
|
Packit |
8f70b4 |
if(s==UNPACK_PREMATURE_EOF) {
|
|
Packit |
8f70b4 |
if(recv_buf->Size()>0) {
|
|
Packit |
8f70b4 |
LogError(2,_("peer unexpectedly closed connection after %s"),recv_buf->Dump());
|
|
Packit |
8f70b4 |
Disconnect(_("peer unexpectedly closed connection"));
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
LogError(4,_("peer closed connection (before handshake)"));
|
|
Packit |
8f70b4 |
Disconnect(_("peer closed connection (before handshake)"));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
Disconnect(_("invalid peer response format"));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!parent->HasMetadata() && !LTEPExtensionEnabled()) {
|
|
Packit |
8f70b4 |
Disconnect("peer cannot provide metadata");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
timeout_timer.Reset();
|
|
Packit |
8f70b4 |
myself=peer_id.eq(Torrent::my_peer_id);
|
|
Packit |
8f70b4 |
if(myself)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
SendExtensions();
|
|
Packit |
8f70b4 |
if(parent->HasMetadata())
|
|
Packit |
8f70b4 |
peer_bitfield=new BitField(parent->total_pieces);
|
|
Packit |
8f70b4 |
if(FastExtensionEnabled()) {
|
|
Packit |
8f70b4 |
if(parent->complete_pieces==0) {
|
|
Packit |
8f70b4 |
LogSend(5,"have-none");
|
|
Packit |
8f70b4 |
Packet(MSG_HAVE_NONE).Pack(send_buf);
|
|
Packit |
8f70b4 |
} else if(parent->complete_pieces==parent->total_pieces) {
|
|
Packit |
8f70b4 |
LogSend(5,"have-all");
|
|
Packit |
8f70b4 |
Packet(MSG_HAVE_ALL).Pack(send_buf);
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
LogSend(5,"bitfield");
|
|
Packit |
8f70b4 |
PacketBitField(parent->my_bitfield).Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
} else if(parent->my_bitfield && parent->my_bitfield->has_any_set()) {
|
|
Packit |
8f70b4 |
LogSend(5,"bitfield");
|
|
Packit |
8f70b4 |
PacketBitField(parent->my_bitfield).Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(Torrent::listener_udp && DHT_Enabled()) {
|
|
Packit |
8f70b4 |
int udp_port=Torrent::listener_udp->GetPort();
|
|
Packit |
8f70b4 |
#if INET6
|
|
Packit |
8f70b4 |
if(Torrent::listener_ipv6_udp && addr.sa.sa_family==AF_INET6)
|
|
Packit |
8f70b4 |
udp_port=Torrent::listener_ipv6_udp->GetPort();
|
|
Packit |
8f70b4 |
#endif
|
|
Packit |
8f70b4 |
if(udp_port) {
|
|
Packit |
8f70b4 |
LogSend(5,xstring::format("port(%d)",udp_port));
|
|
Packit |
8f70b4 |
PacketPort(udp_port).Pack(send_buf);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
keepalive_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(keepalive_timer.Stopped()) {
|
|
Packit |
8f70b4 |
LogSend(5,"keep-alive");
|
|
Packit |
8f70b4 |
Packet(MSG_KEEPALIVE).Pack(send_buf);
|
|
Packit |
8f70b4 |
keepalive_timer.Reset();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(send_buf->Size()>(int)Torrent::BLOCK_SIZE*4)
|
|
Packit |
8f70b4 |
recv_buf->Suspend();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
recv_buf->Resume();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(recv_buf->IsSuspended())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
timeout_timer.Reset(send_buf->EventTime());
|
|
Packit |
8f70b4 |
timeout_timer.Reset(recv_buf->EventTime());
|
|
Packit |
8f70b4 |
if(timeout_timer.Stopped()) {
|
|
Packit |
8f70b4 |
LogError(0,_("Timeout - reconnecting"));
|
|
Packit |
8f70b4 |
Disconnect("timed out");
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!am_interested && interest_timer.Stopped()
|
|
Packit |
8f70b4 |
&& HasNeededPieces() && parent->NeedMoreUploaders())
|
|
Packit |
8f70b4 |
SetAmInterested(true);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(am_interested && sent_queue.count()
|
|
Packit |
8f70b4 |
SendDataRequests();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(peer_interested && am_choking && choke_timer.Stopped()
|
|
Packit |
8f70b4 |
&& parent->AllowMoreDownloaders())
|
|
Packit |
8f70b4 |
SetAmChoking(false);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(recv_queue.count()>0 && send_buf->Size()<(int)Torrent::BLOCK_SIZE*2) {
|
|
Packit |
8f70b4 |
unsigned bytes_allowed=BytesAllowed(RateLimit::PUT);
|
|
Packit |
8f70b4 |
while(bytes_allowed>=recv_queue[0]->req_length) {
|
|
Packit |
8f70b4 |
bytes_allowed-=recv_queue[0]->req_length;
|
|
Packit |
8f70b4 |
SendDataReply();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
if(!Connected())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(recv_queue.count()==0)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
if(send_buf->Size()>=(int)Torrent::BLOCK_SIZE)
|
|
Packit |
8f70b4 |
m|=send_buf->Do();
|
|
Packit |
8f70b4 |
if(send_buf->Size()>=(int)Torrent::BLOCK_SIZE*2)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(recv_buf->Eof() && recv_buf->Size()==0) {
|
|
Packit |
8f70b4 |
LogError(4,_("peer closed connection"));
|
|
Packit |
8f70b4 |
Disconnect(_("peer closed connection"));
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(pex.send_timer.Stopped())
|
|
Packit |
8f70b4 |
SendPEXPeers();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Packet *reply=0;
|
|
Packit |
8f70b4 |
unpack_status_t st=UnpackPacket(recv_buf,&reply);
|
|
Packit |
8f70b4 |
if(st==UNPACK_NO_DATA_YET)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(st!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(st==UNPACK_PREMATURE_EOF) {
|
|
Packit |
8f70b4 |
LogError(2,_("peer unexpectedly closed connection after %s"),recv_buf->Dump());
|
|
Packit |
8f70b4 |
Disconnect(_("peer unexpectedly closed connection"));
|
|
Packit |
8f70b4 |
} else {
|
|
Packit |
8f70b4 |
LogError(2,_("invalid peer response format"));
|
|
Packit |
8f70b4 |
Disconnect(_("invalid peer response format"));
|
|
Packit |
8f70b4 |
}
|
|
Packit |