1 /*
2  * lftp - file transfer program
3  *
4  * Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 
32 #include "Torrent.h"
33 #include "TorrentTracker.h"
34 #include "log.h"
35 #include "url.h"
36 #include "misc.h"
37 #include "plural.h"
38 
AddURL(const char * url)39 void TorrentTracker::AddURL(const char *url)
40 {
41    LogNote(4,"Tracker URL is `%s'",url);
42    ParsedURL u(url,true);
43    if(u.proto.ne("http") && u.proto.ne("https") && u.proto.ne("udp")) {
44       LogError(1,"unsupported tracker protocol `%s', must be http, https or udp",u.proto.get());
45       return;
46    }
47    xstring& tracker_url=*new xstring(url);
48    if(u.proto.ne("udp")) {
49       if(!u.path || !u.path[0])
50 	 tracker_url.append('/');
51       // fix the URL: append either ? or & if missing.
52       if(tracker_url.last_char()!='?' && tracker_url.last_char()!='&')
53 	 tracker_url.append(tracker_url.instr('?')>=0?'&':'?');
54    }
55    tracker_urls.append(&tracker_url);
56 }
57 
TorrentTracker(Torrent * p,const char * url)58 TorrentTracker::TorrentTracker(Torrent *p,const char *url)
59    : parent(p), current_tracker(0),
60      tracker_timer(600), tracker_timeout_timer(120),
61      started(false), tracker_no(0)
62 {
63    AddURL(url);
64 }
65 
IsActive() const66 bool TorrentTracker::IsActive() const
67 {
68    return backend && backend->IsActive();
69 }
Shutdown()70 void TorrentTracker::Shutdown()
71 {
72    if(Failed()) // don't stop a failed tracker
73       return;
74    // stop if have started or at least processing a start request
75    if(started || IsActive())
76       SendTrackerRequest("stopped");
77 }
SetError(const char * e)78 void TorrentTracker::SetError(const char *e)
79 {
80    if(tracker_urls.count()<=1)
81       error=new Error(-1,e,true);
82    else {
83       LogError(3,"Tracker error: %s, using next tracker URL",e);
84       tracker_urls.remove(current_tracker--);
85       NextTracker();
86       // retry immediately
87       tracker_timer.Stop();
88    }
89 }
AddPeerCompact(const char * compact_addr,int len) const90 bool TorrentTracker::AddPeerCompact(const char *compact_addr,int len) const
91 {
92    sockaddr_u a;
93    if(!a.set_compact(compact_addr,len))
94       return false;
95    Enter(parent);
96    parent->AddPeer(new TorrentPeer(parent,&a,tracker_no));
97    Leave(parent);
98    return true;
99 }
AddPeer(const xstring & addr,int port) const100 bool TorrentTracker::AddPeer(const xstring& addr,int port) const
101 {
102    sockaddr_u a;
103 #if INET6
104    if(addr.instr(':')>=0) {
105       a.sa.sa_family=AF_INET6;
106       if(inet_pton(AF_INET6,addr,&a.in6.sin6_addr)<=0)
107 	 return false;
108    } else
109 #endif
110    {
111       a.sa.sa_family=AF_INET;
112       if(!inet_aton(addr,&a.in.sin_addr))
113 	 return false;
114    }
115    a.set_port(port);
116    Enter(parent);
117    parent->AddPeer(new TorrentPeer(parent,&a,tracker_no));
118    Leave(parent);
119    return true;
120 }
Do()121 int TorrentTracker::Do()
122 {
123    int m=STALL;
124    if(Failed())
125       return m;
126    if(backend && backend->IsActive()) {
127       if(tracker_timeout_timer.Stopped()) {
128 	 LogError(3,"Tracker timeout");
129 	 NextTracker();
130 	 return MOVED;
131       }
132    } else {
133       if(tracker_timer.Stopped()) {
134 	 parent->CleanPeers();
135 	 SendTrackerRequest(0);
136       }
137    }
138    return m;
139 }
CreateTrackerBackend()140 void TorrentTracker::CreateTrackerBackend()
141 {
142    backend=0;
143    ParsedURL u(GetURL(),true);
144    if(u.proto.eq("udp")) {
145       backend=new UdpTracker(this,&u);
146    } else if(u.proto.eq("http") || u.proto.eq("https")) {
147       backend=new HttpTracker(this,&u);
148    }
149 }
NextTracker()150 void TorrentTracker::NextTracker()
151 {
152    current_tracker++;
153    if(current_tracker>=tracker_urls.count())
154       current_tracker=0;
155    tracker_timer.Reset();
156 
157    CreateTrackerBackend();
158 }
Start()159 void TorrentTracker::Start()
160 {
161    if(backend || Failed())
162       return;
163    CreateTrackerBackend();
164    SendTrackerRequest("started");
165 }
SendTrackerRequest(const char * event)166 void TorrentTracker::SendTrackerRequest(const char *event)
167 {
168    backend->SendTrackerRequest(event);
169    tracker_timeout_timer.Reset();
170 }
Status() const171 const char *TorrentTracker::Status() const
172 {
173    if(error)
174       return error->Text();
175    if(!backend)
176       return _("not started");
177    if(backend->IsActive())
178       return backend->Status();
179    return xstring::format(_("next request in %s"),NextRequestIn());
180 }
181 
182 
183 // TrackerBackend
GetInfoHash() const184 const xstring& TrackerBackend::GetInfoHash() const { return master->parent->GetInfoHash(); }
GetMyPeerId() const185 const xstring& TrackerBackend::GetMyPeerId() const { return master->parent->GetMyPeerId(); }
GetPort() const186 int TrackerBackend::GetPort() const { return master->parent->GetPort(); }
GetTotalSent() const187 unsigned long long TrackerBackend::GetTotalSent() const { return master->parent->GetTotalSent(); }
GetTotalRecv() const188 unsigned long long TrackerBackend::GetTotalRecv() const { return master->parent->GetTotalRecv(); }
GetTotalLeft() const189 unsigned long long TrackerBackend::GetTotalLeft() const { return master->parent->GetTotalLeft(); }
HasMetadata() const190 bool TrackerBackend::HasMetadata() const { return master->parent->HasMetadata(); }
Complete() const191 bool TrackerBackend::Complete() const { return master->parent->Complete(); }
GetWantedPeersCount() const192 int TrackerBackend::GetWantedPeersCount() const { return master->parent->GetWantedPeersCount(); }
GetMyKey() const193 const xstring& TrackerBackend::GetMyKey() const { return master->parent->GetMyKey(); }
GetMyKeyNum() const194 unsigned TrackerBackend::GetMyKeyNum() const { return master->parent->GetMyKeyNum(); }
GetTrackerId() const195 const char *TrackerBackend::GetTrackerId() const { return master->tracker_id; }
ShuttingDown() const196 bool TrackerBackend::ShuttingDown() const { return master->parent->ShuttingDown(); }
Started() const197 void TrackerBackend::Started() const { master->started=true; }
TrackerRequestFinished() const198 void TrackerBackend::TrackerRequestFinished() const { master->tracker_timer.Reset(); }
199 
200 // HttpTracker
201 #define super TrackerBackend
HandleTrackerReply()202 int HttpTracker::HandleTrackerReply()
203 {
204    if(tracker_reply->Error()) {
205       SetError(tracker_reply->ErrorText());
206       t_session->Close();
207       tracker_reply=0;
208       return MOVED;
209    }
210    if(!tracker_reply->Eof())
211       return STALL;
212    t_session->Close();
213    int rest;
214    Ref<BeNode> reply(BeNode::Parse(tracker_reply->Get(),tracker_reply->Size(),&rest));
215    if(!reply) {
216       LogError(3,"Tracker reply parse error (data: %s)",tracker_reply->Dump());
217       tracker_reply=0;
218       NextTracker();
219       return MOVED;
220    }
221    LogNote(10,"Received tracker reply:");
222    Log::global->Write(10,reply->Format());
223 
224    if(ShuttingDown()) {
225       tracker_reply=0;
226       t_session=0;
227       return MOVED;
228    }
229    Started();
230 
231    if(reply->type!=BeNode::BE_DICT) {
232       SetError("Reply: wrong reply type, must be DICT");
233       tracker_reply=0;
234       return MOVED;
235    }
236 
237    BeNode *b_failure_reason=reply->lookup("failure reason");
238    if(b_failure_reason) {
239       if(b_failure_reason->type==BeNode::BE_STR)
240 	 SetError(b_failure_reason->str);
241       else
242 	 SetError("Reply: wrong `failure reason' type, must be STR");
243       tracker_reply=0;
244       return MOVED;
245    }
246 
247    BeNode *b_interval=reply->lookup("interval",BeNode::BE_INT);
248    if(b_interval)
249       SetInterval(b_interval->num);
250    SetTrackerID(reply->lookup_str("tracker id"));
251 
252    int peers_count=0;
253    BeNode *b_peers=reply->lookup("peers");
254    if(b_peers) {
255       if(b_peers->type==BeNode::BE_STR) { // binary model
256 	 const char *data=b_peers->str;
257 	 int len=b_peers->str.length();
258 	 LogNote(9,"peers have binary model, length=%d",len);
259 	 while(len>=6) {
260 	    if(AddPeerCompact(data,6))
261 	       peers_count++;
262 	    data+=6;
263 	    len-=6;
264 	 }
265       } else if(b_peers->type==BeNode::BE_LIST) { // dictionary model
266 	 int count=b_peers->list.count();
267 	 LogNote(9,"peers have dictionary model, count=%d",count);
268 	 for(int p=0; p<count; p++) {
269 	    BeNode *b_peer=b_peers->list[p];
270 	    if(b_peer->type!=BeNode::BE_DICT)
271 	       continue;
272 	    BeNode *b_ip=b_peer->lookup("ip",BeNode::BE_STR);
273 	    if(!b_ip)
274 	       continue;
275 	    BeNode *b_port=b_peer->lookup("port",BeNode::BE_INT);
276 	    if(!b_port)
277 	       continue;
278 	    if(AddPeer(b_ip->str,b_port->num))
279 	       peers_count++;
280 	 }
281       }
282       LogNote(4,plural("Received valid info about %d peer$|s$",peers_count),peers_count);
283    }
284 #if INET6
285    peers_count=0;
286    b_peers=reply->lookup("peers6",BeNode::BE_STR);
287    if(b_peers) { // binary model
288       const char *data=b_peers->str;
289       int len=b_peers->str.length();
290       while(len>=18) {
291 	 if(AddPeerCompact(data,18))
292 	    peers_count++;
293 	 data+=18;
294 	 len-=18;
295       }
296       LogNote(4,plural("Received valid info about %d IPv6 peer$|s$",peers_count),peers_count);
297    }
298 #endif//INET6
299    tracker_reply=0;
300    TrackerRequestFinished();
301    return MOVED;
302 }
303 
Do()304 int HttpTracker::Do()
305 {
306    int m=STALL;
307    if(!IsActive())
308       return m;
309    if(tracker_reply)
310       m|=HandleTrackerReply();
311    return m;
312 }
313 
SendTrackerRequest(const char * event)314 void HttpTracker::SendTrackerRequest(const char *event)
315 {
316    if(!t_session)
317       return;
318 
319    xstring request(GetURL());
320    request.appendf("info_hash=%s",url::encode(GetInfoHash(),URL_PATH_UNSAFE).get());
321    request.appendf("&peer_id=%s",url::encode(GetMyPeerId(),URL_PATH_UNSAFE).get());
322    request.appendf("&port=%d",GetPort());
323    request.appendf("&uploaded=%llu",GetTotalSent());
324    request.appendf("&downloaded=%llu",GetTotalRecv());
325    request.appendf("&left=%llu",HasMetadata()?GetTotalLeft():123456789ULL);
326    request.append("&compact=1&no_peer_id=1");
327    if(event)
328       request.appendf("&event=%s",event);
329    const char *ip=ResMgr::Query("torrent:ip",0);
330    if(ip && ip[0])
331       request.appendf("&ip=%s",ip);
332 
333 #if INET6
334    int port=Torrent::GetPortIPv4();
335    int port_ipv6=Torrent::GetPortIPv6();
336    const char *ipv6=ResMgr::Query("torrent:ipv6",0);
337    if(port && ip && ip[0])
338       request.appendf("&ipv4=%s:%d",ip,port);
339    if(port_ipv6)
340       request.appendf("&ipv6=[%s]:%d",ipv6&&ipv6[0]?ipv6:Torrent::GetAddressIPv6(),port_ipv6);
341 #endif
342 
343    int numwant=GetWantedPeersCount();
344    if(numwant>=0)
345       request.appendf("&numwant=%d",numwant);
346    const xstring& my_key=GetMyKey();
347    if(my_key)
348       request.appendf("&key=%s",my_key.get());
349    const char *tracker_id=GetTrackerId();
350    if(tracker_id)
351       request.appendf("&trackerid=%s",url::encode(tracker_id,URL_PATH_UNSAFE).get());
352    LogSend(4,request);
353    t_session->Open(url::path_ptr(request),FA::RETRIEVE);
354    t_session->SetFileURL(request);
355    tracker_reply=new IOBufferFileAccess(t_session);
356 }
357 
358 
359 // UdpTracker
Do()360 int UdpTracker::Do()
361 {
362    int m=STALL;
363    if(!peer) {
364       // need to resolve addresses
365       if(!resolver) {
366 	 resolver=new Resolver(hostname,portname,"80");
367 	 resolver->Roll();
368 	 m=MOVED;
369       }
370       if(!resolver->Done())
371 	 return m;
372       if(resolver->Error())
373       {
374 	 SetError(resolver->ErrorMsg());
375 	 return(MOVED);
376       }
377       peer.set(resolver->Result());
378       peer_curr=0;
379       resolver=0;
380       try_number=0;
381       m=MOVED;
382    }
383    if(!IsActive())
384       return m;
385    if(sock==-1) {
386       // need to create the socket
387       sock=SocketCreate(peer[peer_curr].family(),SOCK_DGRAM,IPPROTO_UDP,hostname);
388       if(sock==-1) {
389 	 int saved_errno=errno;
390 	 LogError(9,"socket: %s",strerror(saved_errno));
391 	 if(NonFatalError(saved_errno))
392 	    return m;
393 	 xstring& str=xstring::format(_("cannot create socket of address family %d"),
394 		     peer[peer_curr].family());
395 	 str.appendf(" (%s)",strerror(saved_errno));
396 	 SetError(str);
397 	 return MOVED;
398       }
399    }
400    if(current_action!=a_none) {
401       if(!RecvReply()) {
402 	 if(timeout_timer.Stopped()) {
403 	    LogError(3,"request timeout");
404 	    NextPeer();
405 	    return MOVED;
406 	 }
407 	 return m;
408       }
409       return MOVED;
410    }
411    if(!has_connection_id) {
412       // need to get connection id
413       SendConnectRequest();
414       return MOVED;
415    }
416    SendEventRequest();
417    return MOVED;
418 }
419 
NextPeer()420 void UdpTracker::NextPeer() {
421    current_action=a_none;
422    has_connection_id=false;
423    connection_id=0;
424    int old_peer=peer_curr;
425    peer_curr++;
426    if(peer_curr>=peer.count()) {
427       peer_curr=0;
428       try_number++;
429    }
430    // check if we need to create a socket of different address family
431    if(old_peer!=peer_curr && peer[old_peer].family()!=peer[peer_curr].family()) {
432       close(sock);
433       sock=-1;
434    }
435 }
436 
RecvReply()437 bool UdpTracker::RecvReply() {
438    if(!Ready(sock,POLLIN)) {
439       Block(sock,POLLIN);
440       return false;
441    }
442    Buffer buf;
443    const int max_len=0x1000;
444    sockaddr_u addr;
445    socklen_t addr_len=sizeof(addr);
446    int len=recvfrom(sock,buf.GetSpace(max_len),max_len,0,&addr.sa,&addr_len);
447    if(len<0) {
448       int saved_errno=errno;
449       if(NonFatalError(saved_errno)) {
450 	 Block(sock,POLLIN);
451 	 return false;
452       }
453       SetError(xstring::format("recvfrom: %s",strerror(saved_errno)));
454       return false;
455    }
456    if(len==0) {
457       SetError("recvfrom: EOF?");
458       return false;
459    }
460    buf.SpaceAdd(len);
461    LogRecv(10,xstring::format("got a packet from %s of length %d {%s}",addr.to_string(),len,buf.Dump()));
462    if(len<16) {
463       LogError(9,"ignoring too short packet");
464       return false;
465    }
466    unsigned tid=buf.UnpackUINT32BE(4);
467    if(tid!=transaction_id) {
468       LogError(9,"ignoring mismatching transaction packet (0x%08X!=0x%08X)",tid,transaction_id);
469       return false;
470    }
471    int action=buf.UnpackUINT32BE(0);
472    if(action!=current_action && action!=a_error) {
473       LogError(9,"ignoring mismatching action packet (%d!=%d)",action,current_action);
474       return false;
475    }
476    switch(action) {
477    case a_none:
478       abort();
479    case a_connect:
480       connection_id=buf.UnpackUINT64BE(8);
481       has_connection_id=true;
482       LogNote(9,"connected");
483       break;
484    case a_announce:
485    case a_announce6:
486    {
487       SetInterval(buf.UnpackUINT32BE(8));
488       if(buf.Size()<20)
489 	 break;
490       unsigned leachers=buf.UnpackUINT32BE(12);
491       unsigned seeders=buf.UnpackUINT32BE(16);
492       LogNote(9,"leechers=%u seeders=%u",leachers,seeders);
493       int peers_count=0;
494       int compact_addr_size=6;
495       if(current_action==a_announce6)
496 	 compact_addr_size=18;
497       for(int i=20; i+compact_addr_size<=buf.Size(); i+=compact_addr_size) {
498 	 if(AddPeerCompact(buf.Get()+i,compact_addr_size))
499 	    peers_count++;
500       }
501       LogNote(4,plural("Received valid info about %d peer$|s$",peers_count),peers_count);
502       current_event=ev_idle;
503       TrackerRequestFinished();
504       break;
505    }
506    case a_scrape:
507       // not implemented
508       break;
509    case a_error:
510       SetError(buf.Get()+8);
511       break;
512    }
513    current_action=a_none;
514    try_number=0;
515    return true;
516 }
517 
SendPacket(Buffer & req)518 bool UdpTracker::SendPacket(Buffer& req)
519 {
520    LogSend(10,xstring::format("sending a packet to %s of length %d {%s}",peer[peer_curr].to_string(),req.Size(),req.Dump()));
521    int len=sendto(sock,req.Get(),req.Size(),0,&peer[peer_curr].sa,peer[peer_curr].addr_len());
522    if(len<0) {
523       int saved_errno=errno;
524       if(NonFatalError(saved_errno)) {
525 	 Block(sock,POLLOUT);
526 	 return false;
527       }
528       SetError(xstring::format("sendto: %s",strerror(saved_errno)));
529       return false;
530    }
531    if(len<req.Size()) {
532       LogError(9,"could not send complete datagram of size %d",req.Size());
533       Block(sock,POLLOUT);
534       return false;
535    }
536    timeout_timer.Set(60*(1<<try_number));
537    return true;
538 }
539 
SendConnectRequest()540 bool UdpTracker::SendConnectRequest()
541 {
542    LogNote(9,"connecting...");
543    Buffer req;
544    req.PackUINT64BE(connect_magic);
545    req.PackUINT32BE(a_connect);
546    req.PackUINT32BE(NewTransactionId());
547    if(!SendPacket(req))
548       return false;
549    current_action=a_connect;
550    return true;
551 }
552 
EventToString(event_t e)553 const char *UdpTracker::EventToString(event_t e)
554 {
555    const char *map[]={
556       "",
557       "completed",
558       "started",
559       "stopped",
560    };
561    if(e>=0 && e<=3)
562       return map[e];
563    return "???";
564 }
565 
SendEventRequest()566 bool UdpTracker::SendEventRequest()
567 {
568    action_t action=a_announce;
569    const char *a_name="announce";
570 #if INET6
571    if(peer[peer_curr].family()==AF_INET6) {
572       action=a_announce6;
573       a_name="announce6";
574    }
575 #endif
576    LogNote(9,"%s %s",a_name,EventToString(current_event));
577    assert(has_connection_id);
578    assert(current_event!=ev_idle);
579    Buffer req;
580    req.PackUINT64BE(connection_id);
581    req.PackUINT32BE(action);
582    req.PackUINT32BE(NewTransactionId());
583    req.Append(GetInfoHash());
584    req.Append(GetMyPeerId());
585    req.PackUINT64BE(GetTotalRecv());
586    req.PackUINT64BE(GetTotalLeft());
587    req.PackUINT64BE(GetTotalSent());
588    req.PackUINT32BE(current_event);
589 
590 #if INET6
591    if(action==a_announce6) {
592       const char *ip=ResMgr::Query("torrent:ipv6",0);
593       char ip_packed[16];
594       memset(ip_packed,0,16);
595       if(ip && ip[0])
596 	 inet_pton(AF_INET6,ip,ip_packed);
597       req.Append(ip_packed,16);
598    } else
599 #endif
600    {
601       const char *ip=ResMgr::Query("torrent:ip",0);
602       char ip_packed[4];
603       memset(ip_packed,0,4);
604       if(ip && ip[0])
605 	 inet_pton(AF_INET,ip,ip_packed);
606       req.Append(ip_packed,4);
607    }
608 
609    req.PackUINT32BE(GetMyKeyNum());
610    req.PackUINT32BE(GetWantedPeersCount());
611    req.PackUINT16BE(GetPort());
612 
613    if(!SendPacket(req))
614       return false;
615    current_action=action;
616    return true;
617 }
618 
Status() const619 const char *UdpTracker::Status() const
620 {
621    if(resolver)
622       return(_("Resolving host address..."));
623    if(!has_connection_id)
624       return(_("Connecting..."));
625    if(current_action!=a_none)
626       return _("Waiting for response...");
627    return "";
628 }
629 
SendTrackerRequest(const char * event)630 void UdpTracker::SendTrackerRequest(const char *event)
631 {
632    current_event=ev_none;
633    if(!event)
634       return;
635    if(!strcmp(event,"started"))
636       current_event=ev_started;
637    else if(!strcmp(event,"stopped"))
638       current_event=ev_stopped;
639    else if(!strcmp(event,"completed"))
640       current_event=ev_completed;
641 }
642