Blame src/TorrentTracker.cc

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