|
Packit Service |
a2489d |
/*
|
|
Packit Service |
a2489d |
* lftp - file transfer program
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* This program is free software; you can redistribute it and/or modify
|
|
Packit Service |
a2489d |
* it under the terms of the GNU General Public License as published by
|
|
Packit Service |
a2489d |
* the Free Software Foundation; either version 3 of the License, or
|
|
Packit Service |
a2489d |
* (at your option) any later version.
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
a2489d |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
a2489d |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
a2489d |
* GNU General Public License for more details.
|
|
Packit Service |
a2489d |
*
|
|
Packit Service |
a2489d |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
a2489d |
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit Service |
a2489d |
*/
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
#include <config.h>
|
|
Packit Service |
a2489d |
#include <stdlib.h>
|
|
Packit Service |
a2489d |
#include <assert.h>
|
|
Packit Service |
a2489d |
#include <sys/types.h>
|
|
Packit Service |
a2489d |
#include <sys/stat.h>
|
|
Packit Service |
a2489d |
#include <sys/socket.h>
|
|
Packit Service |
a2489d |
#include <netinet/in.h>
|
|
Packit Service |
a2489d |
#include <arpa/inet.h>
|
|
Packit Service |
a2489d |
#include <unistd.h>
|
|
Packit Service |
a2489d |
#include <fcntl.h>
|
|
Packit Service |
a2489d |
#include <errno.h>
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
#include "Torrent.h"
|
|
Packit Service |
a2489d |
#include "TorrentTracker.h"
|
|
Packit Service |
a2489d |
#include "log.h"
|
|
Packit Service |
a2489d |
#include "url.h"
|
|
Packit Service |
a2489d |
#include "misc.h"
|
|
Packit Service |
a2489d |
#include "plural.h"
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
void TorrentTracker::AddURL(const char *url)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
LogNote(4,"Tracker URL is `%s'",url);
|
|
Packit Service |
a2489d |
ParsedURL u(url,true);
|
|
Packit Service |
a2489d |
if(u.proto.ne("http") && u.proto.ne("https") && u.proto.ne("udp")) {
|
|
Packit Service |
a2489d |
LogError(1,"unsupported tracker protocol `%s', must be http, https or udp",u.proto.get());
|
|
Packit Service |
a2489d |
return;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
xstring& tracker_url=*new xstring(url);
|
|
Packit Service |
a2489d |
if(u.proto.ne("udp")) {
|
|
Packit Service |
a2489d |
if(!u.path || !u.path[0])
|
|
Packit Service |
a2489d |
tracker_url.append('/');
|
|
Packit Service |
a2489d |
// fix the URL: append either ? or & if missing.
|
|
Packit Service |
a2489d |
if(tracker_url.last_char()!='?' && tracker_url.last_char()!='&')
|
|
Packit Service |
a2489d |
tracker_url.append(tracker_url.instr('?')>=0?'&':'?');
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
tracker_urls.append(&tracker_url);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
TorrentTracker::TorrentTracker(Torrent *p,const char *url)
|
|
Packit Service |
a2489d |
: parent(p), current_tracker(0),
|
|
Packit Service |
a2489d |
tracker_timer(600), tracker_timeout_timer(120),
|
|
Packit Service |
a2489d |
started(false), tracker_no(0)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
AddURL(url);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
bool TorrentTracker::IsActive() const
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
return backend && backend->IsActive();
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::Shutdown()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(Failed()) // don't stop a failed tracker
|
|
Packit Service |
a2489d |
return;
|
|
Packit Service |
a2489d |
// stop if have started or at least processing a start request
|
|
Packit Service |
a2489d |
if(started || IsActive())
|
|
Packit Service |
a2489d |
SendTrackerRequest("stopped");
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::SetError(const char *e)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(tracker_urls.count()<=1)
|
|
Packit Service |
a2489d |
error=new Error(-1,e,true);
|
|
Packit Service |
a2489d |
else {
|
|
Packit Service |
a2489d |
LogError(3,"Tracker error: %s, using next tracker URL",e);
|
|
Packit Service |
a2489d |
tracker_urls.remove(current_tracker--);
|
|
Packit Service |
a2489d |
NextTracker();
|
|
Packit Service |
a2489d |
// retry immediately
|
|
Packit Service |
a2489d |
tracker_timer.Stop();
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
bool TorrentTracker::AddPeerCompact(const char *compact_addr,int len) const
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
sockaddr_u a;
|
|
Packit Service |
a2489d |
if(!a.set_compact(compact_addr,len))
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
Enter(parent);
|
|
Packit Service |
a2489d |
parent->AddPeer(new TorrentPeer(parent,&a,tracker_no));
|
|
Packit Service |
a2489d |
Leave(parent);
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
bool TorrentTracker::AddPeer(const xstring& addr,int port) const
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
sockaddr_u a;
|
|
Packit Service |
a2489d |
#if INET6
|
|
Packit Service |
a2489d |
if(addr.instr(':')>=0) {
|
|
Packit Service |
a2489d |
a.sa.sa_family=AF_INET6;
|
|
Packit Service |
a2489d |
if(inet_pton(AF_INET6,addr,&a.in6.sin6_addr)<=0)
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
} else
|
|
Packit Service |
a2489d |
#endif
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
a.sa.sa_family=AF_INET;
|
|
Packit Service |
a2489d |
if(!inet_aton(addr,&a.in.sin_addr))
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
a.set_port(port);
|
|
Packit Service |
a2489d |
Enter(parent);
|
|
Packit Service |
a2489d |
parent->AddPeer(new TorrentPeer(parent,&a,tracker_no));
|
|
Packit Service |
a2489d |
Leave(parent);
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
int TorrentTracker::Do()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
int m=STALL;
|
|
Packit Service |
a2489d |
if(Failed())
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
if(backend && backend->IsActive()) {
|
|
Packit Service |
a2489d |
if(tracker_timeout_timer.Stopped()) {
|
|
Packit Service |
a2489d |
LogError(3,"Tracker timeout");
|
|
Packit Service |
a2489d |
NextTracker();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
} else {
|
|
Packit Service |
a2489d |
if(tracker_timer.Stopped()) {
|
|
Packit Service |
a2489d |
parent->CleanPeers();
|
|
Packit Service |
a2489d |
SendTrackerRequest(0);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::CreateTrackerBackend()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
backend=0;
|
|
Packit Service |
a2489d |
ParsedURL u(GetURL(),true);
|
|
Packit Service |
a2489d |
if(u.proto.eq("udp")) {
|
|
Packit Service |
a2489d |
backend=new UdpTracker(this,&u);
|
|
Packit Service |
a2489d |
} else if(u.proto.eq("http") || u.proto.eq("https")) {
|
|
Packit Service |
a2489d |
backend=new HttpTracker(this,&u);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::NextTracker()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
current_tracker++;
|
|
Packit Service |
a2489d |
if(current_tracker>=tracker_urls.count())
|
|
Packit Service |
a2489d |
current_tracker=0;
|
|
Packit Service |
a2489d |
tracker_timer.Reset();
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
CreateTrackerBackend();
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::Start()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(backend || Failed())
|
|
Packit Service |
a2489d |
return;
|
|
Packit Service |
a2489d |
CreateTrackerBackend();
|
|
Packit Service |
a2489d |
SendTrackerRequest("started");
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
void TorrentTracker::SendTrackerRequest(const char *event)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
backend->SendTrackerRequest(event);
|
|
Packit Service |
a2489d |
tracker_timeout_timer.Reset();
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
const char *TorrentTracker::Status() const
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(error)
|
|
Packit Service |
a2489d |
return error->Text();
|
|
Packit Service |
a2489d |
if(!backend)
|
|
Packit Service |
a2489d |
return _("not started");
|
|
Packit Service |
a2489d |
if(backend->IsActive())
|
|
Packit Service |
a2489d |
return backend->Status();
|
|
Packit Service |
a2489d |
return xstring::format(_("next request in %s"),NextRequestIn());
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
// TrackerBackend
|
|
Packit Service |
a2489d |
const xstring& TrackerBackend::GetInfoHash() const { return master->parent->GetInfoHash(); }
|
|
Packit Service |
a2489d |
const xstring& TrackerBackend::GetMyPeerId() const { return master->parent->GetMyPeerId(); }
|
|
Packit Service |
a2489d |
int TrackerBackend::GetPort() const { return master->parent->GetPort(); }
|
|
Packit Service |
a2489d |
unsigned long long TrackerBackend::GetTotalSent() const { return master->parent->GetTotalSent(); }
|
|
Packit Service |
a2489d |
unsigned long long TrackerBackend::GetTotalRecv() const { return master->parent->GetTotalRecv(); }
|
|
Packit Service |
a2489d |
unsigned long long TrackerBackend::GetTotalLeft() const { return master->parent->GetTotalLeft(); }
|
|
Packit Service |
a2489d |
bool TrackerBackend::HasMetadata() const { return master->parent->HasMetadata(); }
|
|
Packit Service |
a2489d |
bool TrackerBackend::Complete() const { return master->parent->Complete(); }
|
|
Packit Service |
a2489d |
int TrackerBackend::GetWantedPeersCount() const { return master->parent->GetWantedPeersCount(); }
|
|
Packit Service |
a2489d |
const xstring& TrackerBackend::GetMyKey() const { return master->parent->GetMyKey(); }
|
|
Packit Service |
a2489d |
unsigned TrackerBackend::GetMyKeyNum() const { return master->parent->GetMyKeyNum(); }
|
|
Packit Service |
a2489d |
const char *TrackerBackend::GetTrackerId() const { return master->tracker_id; }
|
|
Packit Service |
a2489d |
bool TrackerBackend::ShuttingDown() const { return master->parent->ShuttingDown(); }
|
|
Packit Service |
a2489d |
void TrackerBackend::Started() const { master->started=true; }
|
|
Packit Service |
a2489d |
void TrackerBackend::TrackerRequestFinished() const { master->tracker_timer.Reset(); }
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
// HttpTracker
|
|
Packit Service |
a2489d |
#define super TrackerBackend
|
|
Packit Service |
a2489d |
int HttpTracker::HandleTrackerReply()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(tracker_reply->Error()) {
|
|
Packit Service |
a2489d |
SetError(tracker_reply->ErrorText());
|
|
Packit Service |
a2489d |
t_session->Close();
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(!tracker_reply->Eof())
|
|
Packit Service |
a2489d |
return STALL;
|
|
Packit Service |
a2489d |
t_session->Close();
|
|
Packit Service |
a2489d |
int rest;
|
|
Packit Service |
a2489d |
Ref<BeNode> reply(BeNode::Parse(tracker_reply->Get(),tracker_reply->Size(),&rest));
|
|
Packit Service |
a2489d |
if(!reply) {
|
|
Packit Service |
a2489d |
LogError(3,"Tracker reply parse error (data: %s)",tracker_reply->Dump());
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
NextTracker();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
LogNote(10,"Received tracker reply:");
|
|
Packit Service |
a2489d |
Log::global->Write(10,reply->Format());
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(ShuttingDown()) {
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
t_session=0;
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
Started();
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(reply->type!=BeNode::BE_DICT) {
|
|
Packit Service |
a2489d |
SetError("Reply: wrong reply type, must be DICT");
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
BeNode *b_failure_reason=reply->lookup("failure reason");
|
|
Packit Service |
a2489d |
if(b_failure_reason) {
|
|
Packit Service |
a2489d |
if(b_failure_reason->type==BeNode::BE_STR)
|
|
Packit Service |
a2489d |
SetError(b_failure_reason->str);
|
|
Packit Service |
a2489d |
else
|
|
Packit Service |
a2489d |
SetError("Reply: wrong `failure reason' type, must be STR");
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
BeNode *b_interval=reply->lookup("interval",BeNode::BE_INT);
|
|
Packit Service |
a2489d |
if(b_interval)
|
|
Packit Service |
a2489d |
SetInterval(b_interval->num);
|
|
Packit Service |
a2489d |
SetTrackerID(reply->lookup_str("tracker id"));
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
int peers_count=0;
|
|
Packit Service |
a2489d |
BeNode *b_peers=reply->lookup("peers");
|
|
Packit Service |
a2489d |
if(b_peers) {
|
|
Packit Service |
a2489d |
if(b_peers->type==BeNode::BE_STR) { // binary model
|
|
Packit Service |
a2489d |
const char *data=b_peers->str;
|
|
Packit Service |
a2489d |
int len=b_peers->str.length();
|
|
Packit Service |
a2489d |
LogNote(9,"peers have binary model, length=%d",len);
|
|
Packit Service |
a2489d |
while(len>=6) {
|
|
Packit Service |
a2489d |
if(AddPeerCompact(data,6))
|
|
Packit Service |
a2489d |
peers_count++;
|
|
Packit Service |
a2489d |
data+=6;
|
|
Packit Service |
a2489d |
len-=6;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
} else if(b_peers->type==BeNode::BE_LIST) { // dictionary model
|
|
Packit Service |
a2489d |
int count=b_peers->list.count();
|
|
Packit Service |
a2489d |
LogNote(9,"peers have dictionary model, count=%d",count);
|
|
Packit Service |
a2489d |
for(int p=0; p
|
|
Packit Service |
a2489d |
BeNode *b_peer=b_peers->list[p];
|
|
Packit Service |
a2489d |
if(b_peer->type!=BeNode::BE_DICT)
|
|
Packit Service |
a2489d |
continue;
|
|
Packit Service |
a2489d |
BeNode *b_ip=b_peer->lookup("ip",BeNode::BE_STR);
|
|
Packit Service |
a2489d |
if(!b_ip)
|
|
Packit Service |
a2489d |
continue;
|
|
Packit Service |
a2489d |
BeNode *b_port=b_peer->lookup("port",BeNode::BE_INT);
|
|
Packit Service |
a2489d |
if(!b_port)
|
|
Packit Service |
a2489d |
continue;
|
|
Packit Service |
a2489d |
if(AddPeer(b_ip->str,b_port->num))
|
|
Packit Service |
a2489d |
peers_count++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
LogNote(4,plural("Received valid info about %d peer$|s$",peers_count),peers_count);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
#if INET6
|
|
Packit Service |
a2489d |
peers_count=0;
|
|
Packit Service |
a2489d |
b_peers=reply->lookup("peers6",BeNode::BE_STR);
|
|
Packit Service |
a2489d |
if(b_peers) { // binary model
|
|
Packit Service |
a2489d |
const char *data=b_peers->str;
|
|
Packit Service |
a2489d |
int len=b_peers->str.length();
|
|
Packit Service |
a2489d |
while(len>=18) {
|
|
Packit Service |
a2489d |
if(AddPeerCompact(data,18))
|
|
Packit Service |
a2489d |
peers_count++;
|
|
Packit Service |
a2489d |
data+=18;
|
|
Packit Service |
a2489d |
len-=18;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
LogNote(4,plural("Received valid info about %d IPv6 peer$|s$",peers_count),peers_count);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
#endif//INET6
|
|
Packit Service |
a2489d |
tracker_reply=0;
|
|
Packit Service |
a2489d |
TrackerRequestFinished();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
int HttpTracker::Do()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
int m=STALL;
|
|
Packit Service |
a2489d |
if(!IsActive())
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
if(tracker_reply)
|
|
Packit Service |
a2489d |
m|=HandleTrackerReply();
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
void HttpTracker::SendTrackerRequest(const char *event)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(!t_session)
|
|
Packit Service |
a2489d |
return;
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
xstring request(GetURL());
|
|
Packit Service |
a2489d |
request.appendf("info_hash=%s",url::encode(GetInfoHash(),URL_PATH_UNSAFE).get());
|
|
Packit Service |
a2489d |
request.appendf("&peer_id=%s",url::encode(GetMyPeerId(),URL_PATH_UNSAFE).get());
|
|
Packit Service |
a2489d |
request.appendf("&port=%d",GetPort());
|
|
Packit Service |
a2489d |
request.appendf("&uploaded=%llu",GetTotalSent());
|
|
Packit Service |
a2489d |
request.appendf("&downloaded=%llu",GetTotalRecv());
|
|
Packit Service |
a2489d |
request.appendf("&left=%llu",HasMetadata()?GetTotalLeft():123456789ULL);
|
|
Packit Service |
a2489d |
request.append("&compact=1&no_peer_id=1");
|
|
Packit Service |
a2489d |
if(event)
|
|
Packit Service |
a2489d |
request.appendf("&event=%s",event);
|
|
Packit Service |
a2489d |
const char *ip=ResMgr::Query("torrent:ip",0);
|
|
Packit Service |
a2489d |
if(ip && ip[0])
|
|
Packit Service |
a2489d |
request.appendf("&ip=%s",ip);
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
#if INET6
|
|
Packit Service |
a2489d |
int port=Torrent::GetPortIPv4();
|
|
Packit Service |
a2489d |
int port_ipv6=Torrent::GetPortIPv6();
|
|
Packit Service |
a2489d |
const char *ipv6=ResMgr::Query("torrent:ipv6",0);
|
|
Packit Service |
a2489d |
if(port && ip && ip[0])
|
|
Packit Service |
a2489d |
request.appendf("&ipv4=%s:%d",ip,port);
|
|
Packit Service |
a2489d |
if(port_ipv6)
|
|
Packit Service |
a2489d |
request.appendf("&ipv6=[%s]:%d",ipv6&&ipv6[0]?ipv6:Torrent::GetAddressIPv6(),port_ipv6);
|
|
Packit Service |
a2489d |
#endif
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
int numwant=GetWantedPeersCount();
|
|
Packit Service |
a2489d |
if(numwant>=0)
|
|
Packit Service |
a2489d |
request.appendf("&numwant=%d",numwant);
|
|
Packit Service |
a2489d |
const xstring& my_key=GetMyKey();
|
|
Packit Service |
a2489d |
if(my_key)
|
|
Packit Service |
a2489d |
request.appendf("&key=%s",my_key.get());
|
|
Packit Service |
a2489d |
const char *tracker_id=GetTrackerId();
|
|
Packit Service |
a2489d |
if(tracker_id)
|
|
Packit Service |
a2489d |
request.appendf("&trackerid=%s",url::encode(tracker_id,URL_PATH_UNSAFE).get());
|
|
Packit Service |
a2489d |
LogSend(4,request);
|
|
Packit Service |
a2489d |
t_session->Open(url::path_ptr(request),FA::RETRIEVE);
|
|
Packit Service |
a2489d |
t_session->SetFileURL(request);
|
|
Packit Service |
a2489d |
tracker_reply=new IOBufferFileAccess(t_session);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
// UdpTracker
|
|
Packit Service |
a2489d |
int UdpTracker::Do()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
int m=STALL;
|
|
Packit Service |
a2489d |
if(!peer) {
|
|
Packit Service |
a2489d |
// need to resolve addresses
|
|
Packit Service |
a2489d |
if(!resolver) {
|
|
Packit Service |
a2489d |
resolver=new Resolver(hostname,portname,"80");
|
|
Packit Service |
a2489d |
resolver->Roll();
|
|
Packit Service |
a2489d |
m=MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(!resolver->Done())
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
if(resolver->Error())
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
SetError(resolver->ErrorMsg());
|
|
Packit Service |
a2489d |
return(MOVED);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
peer.set(resolver->Result());
|
|
Packit Service |
a2489d |
peer_curr=0;
|
|
Packit Service |
a2489d |
resolver=0;
|
|
Packit Service |
a2489d |
try_number=0;
|
|
Packit Service |
a2489d |
m=MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(!IsActive())
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
if(sock==-1) {
|
|
Packit Service |
a2489d |
// need to create the socket
|
|
Packit Service |
a2489d |
sock=SocketCreate(peer[peer_curr].family(),SOCK_DGRAM,IPPROTO_UDP,hostname);
|
|
Packit Service |
a2489d |
if(sock==-1) {
|
|
Packit Service |
a2489d |
int saved_errno=errno;
|
|
Packit Service |
a2489d |
LogError(9,"socket: %s",strerror(saved_errno));
|
|
Packit Service |
a2489d |
if(NonFatalError(saved_errno))
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
xstring& str=xstring::format(_("cannot create socket of address family %d"),
|
|
Packit Service |
a2489d |
peer[peer_curr].family());
|
|
Packit Service |
a2489d |
str.appendf(" (%s)",strerror(saved_errno));
|
|
Packit Service |
a2489d |
SetError(str);
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(current_action!=a_none) {
|
|
Packit Service |
a2489d |
if(!RecvReply()) {
|
|
Packit Service |
a2489d |
if(timeout_timer.Stopped()) {
|
|
Packit Service |
a2489d |
LogError(3,"request timeout");
|
|
Packit Service |
a2489d |
NextPeer();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
return m;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(!has_connection_id) {
|
|
Packit Service |
a2489d |
// need to get connection id
|
|
Packit Service |
a2489d |
SendConnectRequest();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
SendEventRequest();
|
|
Packit Service |
a2489d |
return MOVED;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
void UdpTracker::NextPeer() {
|
|
Packit Service |
a2489d |
current_action=a_none;
|
|
Packit Service |
a2489d |
has_connection_id=false;
|
|
Packit Service |
a2489d |
connection_id=0;
|
|
Packit Service |
a2489d |
int old_peer=peer_curr;
|
|
Packit Service |
a2489d |
peer_curr++;
|
|
Packit Service |
a2489d |
if(peer_curr>=peer.count()) {
|
|
Packit Service |
a2489d |
peer_curr=0;
|
|
Packit Service |
a2489d |
try_number++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
// check if we need to create a socket of different address family
|
|
Packit Service |
a2489d |
if(old_peer!=peer_curr && peer[old_peer].family()!=peer[peer_curr].family()) {
|
|
Packit Service |
a2489d |
close(sock);
|
|
Packit Service |
a2489d |
sock=-1;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
bool UdpTracker::RecvReply() {
|
|
Packit Service |
a2489d |
if(!Ready(sock,POLLIN)) {
|
|
Packit Service |
a2489d |
Block(sock,POLLIN);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
Buffer buf;
|
|
Packit Service |
a2489d |
const int max_len=0x1000;
|
|
Packit Service |
a2489d |
sockaddr_u addr;
|
|
Packit Service |
a2489d |
socklen_t addr_len=sizeof(addr);
|
|
Packit Service |
a2489d |
int len=recvfrom(sock,buf.GetSpace(max_len),max_len,0,&addr.sa,&addr_len);
|
|
Packit Service |
a2489d |
if(len<0) {
|
|
Packit Service |
a2489d |
int saved_errno=errno;
|
|
Packit Service |
a2489d |
if(NonFatalError(saved_errno)) {
|
|
Packit Service |
a2489d |
Block(sock,POLLIN);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
SetError(xstring::format("recvfrom: %s",strerror(saved_errno)));
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(len==0) {
|
|
Packit Service |
a2489d |
SetError("recvfrom: EOF?");
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
buf.SpaceAdd(len);
|
|
Packit Service |
a2489d |
LogRecv(10,xstring::format("got a packet from %s of length %d {%s}",addr.to_string(),len,buf.Dump()));
|
|
Packit Service |
a2489d |
if(len<16) {
|
|
Packit Service |
a2489d |
LogError(9,"ignoring too short packet");
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
unsigned tid=buf.UnpackUINT32BE(4);
|
|
Packit Service |
a2489d |
if(tid!=transaction_id) {
|
|
Packit Service |
a2489d |
LogError(9,"ignoring mismatching transaction packet (0x%08X!=0x%08X)",tid,transaction_id);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
int action=buf.UnpackUINT32BE(0);
|
|
Packit Service |
a2489d |
if(action!=current_action && action!=a_error) {
|
|
Packit Service |
a2489d |
LogError(9,"ignoring mismatching action packet (%d!=%d)",action,current_action);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
switch(action) {
|
|
Packit Service |
a2489d |
case a_none:
|
|
Packit Service |
a2489d |
abort();
|
|
Packit Service |
a2489d |
case a_connect:
|
|
Packit Service |
a2489d |
connection_id=buf.UnpackUINT64BE(8);
|
|
Packit Service |
a2489d |
has_connection_id=true;
|
|
Packit Service |
a2489d |
LogNote(9,"connected");
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
case a_announce:
|
|
Packit Service |
a2489d |
case a_announce6:
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
SetInterval(buf.UnpackUINT32BE(8));
|
|
Packit Service |
a2489d |
if(buf.Size()<20)
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
unsigned leachers=buf.UnpackUINT32BE(12);
|
|
Packit Service |
a2489d |
unsigned seeders=buf.UnpackUINT32BE(16);
|
|
Packit Service |
a2489d |
LogNote(9,"leechers=%u seeders=%u",leachers,seeders);
|
|
Packit Service |
a2489d |
int peers_count=0;
|
|
Packit Service |
a2489d |
int compact_addr_size=6;
|
|
Packit Service |
a2489d |
if(current_action==a_announce6)
|
|
Packit Service |
a2489d |
compact_addr_size=18;
|
|
Packit Service |
a2489d |
for(int i=20; i+compact_addr_size<=buf.Size(); i+=compact_addr_size) {
|
|
Packit Service |
a2489d |
if(AddPeerCompact(buf.Get()+i,compact_addr_size))
|
|
Packit Service |
a2489d |
peers_count++;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
LogNote(4,plural("Received valid info about %d peer$|s$",peers_count),peers_count);
|
|
Packit Service |
a2489d |
current_event=ev_idle;
|
|
Packit Service |
a2489d |
TrackerRequestFinished();
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
case a_scrape:
|
|
Packit Service |
a2489d |
// not implemented
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
case a_error:
|
|
Packit Service |
a2489d |
SetError(buf.Get()+8);
|
|
Packit Service |
a2489d |
break;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
current_action=a_none;
|
|
Packit Service |
a2489d |
try_number=0;
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
bool UdpTracker::SendPacket(Buffer& req)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
LogSend(10,xstring::format("sending a packet to %s of length %d {%s}",peer[peer_curr].to_string(),req.Size(),req.Dump()));
|
|
Packit Service |
a2489d |
int len=sendto(sock,req.Get(),req.Size(),0,&peer[peer_curr].sa,peer[peer_curr].addr_len());
|
|
Packit Service |
a2489d |
if(len<0) {
|
|
Packit Service |
a2489d |
int saved_errno=errno;
|
|
Packit Service |
a2489d |
if(NonFatalError(saved_errno)) {
|
|
Packit Service |
a2489d |
Block(sock,POLLOUT);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
SetError(xstring::format("sendto: %s",strerror(saved_errno)));
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
if(len
|
|
Packit Service |
a2489d |
LogError(9,"could not send complete datagram of size %d",req.Size());
|
|
Packit Service |
a2489d |
Block(sock,POLLOUT);
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
timeout_timer.Set(60*(1<
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
bool UdpTracker::SendConnectRequest()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
LogNote(9,"connecting...");
|
|
Packit Service |
a2489d |
Buffer req;
|
|
Packit Service |
a2489d |
req.PackUINT64BE(connect_magic);
|
|
Packit Service |
a2489d |
req.PackUINT32BE(a_connect);
|
|
Packit Service |
a2489d |
req.PackUINT32BE(NewTransactionId());
|
|
Packit Service |
a2489d |
if(!SendPacket(req))
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
current_action=a_connect;
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
const char *UdpTracker::EventToString(event_t e)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
const char *map[]={
|
|
Packit Service |
a2489d |
"",
|
|
Packit Service |
a2489d |
"completed",
|
|
Packit Service |
a2489d |
"started",
|
|
Packit Service |
a2489d |
"stopped",
|
|
Packit Service |
a2489d |
};
|
|
Packit Service |
a2489d |
if(e>=0 && e<=3)
|
|
Packit Service |
a2489d |
return map[e];
|
|
Packit Service |
a2489d |
return "???";
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
bool UdpTracker::SendEventRequest()
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
action_t action=a_announce;
|
|
Packit Service |
a2489d |
const char *a_name="announce";
|
|
Packit Service |
a2489d |
#if INET6
|
|
Packit Service |
a2489d |
if(peer[peer_curr].family()==AF_INET6) {
|
|
Packit Service |
a2489d |
action=a_announce6;
|
|
Packit Service |
a2489d |
a_name="announce6";
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
#endif
|
|
Packit Service |
a2489d |
LogNote(9,"%s %s",a_name,EventToString(current_event));
|
|
Packit Service |
a2489d |
assert(has_connection_id);
|
|
Packit Service |
a2489d |
assert(current_event!=ev_idle);
|
|
Packit Service |
a2489d |
Buffer req;
|
|
Packit Service |
a2489d |
req.PackUINT64BE(connection_id);
|
|
Packit Service |
a2489d |
req.PackUINT32BE(action);
|
|
Packit Service |
a2489d |
req.PackUINT32BE(NewTransactionId());
|
|
Packit Service |
a2489d |
req.Append(GetInfoHash());
|
|
Packit Service |
a2489d |
req.Append(GetMyPeerId());
|
|
Packit Service |
a2489d |
req.PackUINT64BE(GetTotalRecv());
|
|
Packit Service |
a2489d |
req.PackUINT64BE(GetTotalLeft());
|
|
Packit Service |
a2489d |
req.PackUINT64BE(GetTotalSent());
|
|
Packit Service |
a2489d |
req.PackUINT32BE(current_event);
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
#if INET6
|
|
Packit Service |
a2489d |
if(action==a_announce6) {
|
|
Packit Service |
a2489d |
const char *ip=ResMgr::Query("torrent:ipv6",0);
|
|
Packit Service |
a2489d |
char ip_packed[16];
|
|
Packit Service |
a2489d |
memset(ip_packed,0,16);
|
|
Packit Service |
a2489d |
if(ip && ip[0])
|
|
Packit Service |
a2489d |
inet_pton(AF_INET6,ip,ip_packed);
|
|
Packit Service |
a2489d |
req.Append(ip_packed,16);
|
|
Packit Service |
a2489d |
} else
|
|
Packit Service |
a2489d |
#endif
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
const char *ip=ResMgr::Query("torrent:ip",0);
|
|
Packit Service |
a2489d |
char ip_packed[4];
|
|
Packit Service |
a2489d |
memset(ip_packed,0,4);
|
|
Packit Service |
a2489d |
if(ip && ip[0])
|
|
Packit Service |
a2489d |
inet_pton(AF_INET,ip,ip_packed);
|
|
Packit Service |
a2489d |
req.Append(ip_packed,4);
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
req.PackUINT32BE(GetMyKeyNum());
|
|
Packit Service |
a2489d |
req.PackUINT32BE(GetWantedPeersCount());
|
|
Packit Service |
a2489d |
req.PackUINT16BE(GetPort());
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
if(!SendPacket(req))
|
|
Packit Service |
a2489d |
return false;
|
|
Packit Service |
a2489d |
current_action=action;
|
|
Packit Service |
a2489d |
return true;
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
const char *UdpTracker::Status() const
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
if(resolver)
|
|
Packit Service |
a2489d |
return(_("Resolving host address..."));
|
|
Packit Service |
a2489d |
if(!has_connection_id)
|
|
Packit Service |
a2489d |
return(_("Connecting..."));
|
|
Packit Service |
a2489d |
if(current_action!=a_none)
|
|
Packit Service |
a2489d |
return _("Waiting for response...");
|
|
Packit Service |
a2489d |
return "";
|
|
Packit Service |
a2489d |
}
|
|
Packit Service |
a2489d |
|
|
Packit Service |
a2489d |
void UdpTracker::SendTrackerRequest(const char *event)
|
|
Packit Service |
a2489d |
{
|
|
Packit Service |
a2489d |
current_event=ev_none;
|
|
Packit Service |
a2489d |
if(!event)
|
|
Packit Service |
a2489d |
return;
|
|
Packit Service |
a2489d |
if(!strcmp(event,"started"))
|
|
Packit Service |
a2489d |
current_event=ev_started;
|
|
Packit Service |
a2489d |
else if(!strcmp(event,"stopped"))
|
|
Packit Service |
a2489d |
current_event=ev_stopped;
|
|
Packit Service |
a2489d |
else if(!strcmp(event,"completed"))
|
|
Packit Service |
a2489d |
current_event=ev_completed;
|
|
Packit Service |
a2489d |
}
|