apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

ftp.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  FTP Acquire Method - This is the FTP acquire method for APT.
6 
7  This is a very simple implementation that does not try to optimize
8  at all. Commands are sent synchronously with the FTP server (as the
9  rfc recommends, but it is not really necessary..) and no tricks are
10  done to speed things along.
11 
12  RFC 2428 describes the IPv6 FTP behavior
13 
14  ##################################################################### */
15  /*}}}*/
16 // Include Files /*{{{*/
17 #include <config.h>
18 
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/fileutl.h>
22 #include <apt-pkg/hashes.h>
23 #include <apt-pkg/strutl.h>
24 
25 #include <iostream>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <unistd.h>
36 
37 // Internet stuff
38 #include <netdb.h>
39 #include <arpa/inet.h>
40 #include <netinet/in.h>
41 
42 #include "connect.h"
43 #include "ftp.h"
44 #include "rfc2553emu.h"
45 
46 #include <apti18n.h>
47  /*}}}*/
48 
49 using namespace std;
50 
51 /* This table is for the EPRT and EPSV commands, it maps the OS address
52  family to the IETF address families */
53 struct AFMap
54 {
55  unsigned long Family;
56  unsigned long IETFFamily;
57 };
58 
59 #ifndef AF_INET6
60 struct AFMap AFMap[] = {{AF_INET,1},{0, 0}};
61 #else
62 struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{0, 0}};
63 #endif
64 
65 unsigned long TimeOut = 30;
67 string FtpMethod::FailFile;
68 int FtpMethod::FailFd = -1;
69 time_t FtpMethod::FailTime = 0;
70 
71 // FTPConn::FTPConn - Constructor /*{{{*/
72 // ---------------------------------------------------------------------
73 /* */
74 FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(MethodFd::FromFd(-1)), DataFd(-1),
75  DataListenFd(-1), ServerName(Srv),
76  ForceExtended(false), TryPassive(true),
77  PeerAddrLen(0), ServerAddrLen(0)
78 {
79  Debug = _config->FindB("Debug::Acquire::Ftp",false);
80  PasvAddr = 0;
81  Buffer[0] = '\0';
82 }
83  /*}}}*/
84 // FTPConn::~FTPConn - Destructor /*{{{*/
85 // ---------------------------------------------------------------------
86 /* */
88 {
89  Close();
90 }
91  /*}}}*/
92 // FTPConn::Close - Close down the connection /*{{{*/
93 // ---------------------------------------------------------------------
94 /* Just tear down the socket and data socket */
96 {
97  ServerFd->Close();
98  close(DataFd);
99  DataFd = -1;
100  close(DataListenFd);
101  DataListenFd = -1;
102 
103  if (PasvAddr != 0)
105  PasvAddr = 0;
106 }
107  /*}}}*/
108 // FTPConn::Open - Open a new connection /*{{{*/
109 // ---------------------------------------------------------------------
110 /* Connect to the server using a non-blocking connection and perform a
111  login. */
113 {
114  // Use the already open connection if possible.
115  if (ServerFd->Fd() != -1)
117 
118  Close();
119 
120  // Determine the proxy setting
121  string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host);
122  if (!SpecificProxy.empty())
123  {
124  if (SpecificProxy == "DIRECT")
125  Proxy = "";
126  else
127  Proxy = SpecificProxy;
128  }
129  else
130  {
131  string DefProxy = _config->Find("Acquire::ftp::Proxy");
132  if (!DefProxy.empty())
133  {
134  Proxy = DefProxy;
135  }
136  else
137  {
138  char* result = getenv("ftp_proxy");
139  Proxy = result ? result : "";
140  }
141  }
142 
143  // Parse no_proxy, a , separated list of domains
144  if (getenv("no_proxy") != 0)
145  {
146  if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
147  Proxy = "";
148  }
149 
150  // Determine what host and port to use based on the proxy settings
151  int Port = 0;
152  string Host;
153  if (Proxy.empty() == true)
154  {
155  if (ServerName.Port != 0)
156  Port = ServerName.Port;
157  Host = ServerName.Host;
158  }
159  else
160  {
161  if (Proxy.Port != 0)
162  Port = Proxy.Port;
163  Host = Proxy.Host;
164  }
165 
166  /* Connect to the remote server. Since FTP is connection oriented we
167  want to make sure we get a new server every time we reconnect */
168  RotateDNS();
169  auto result = Connect(Host, Port, "ftp", 21, ServerFd, TimeOut, Owner);
170  if (result != ResultState::SUCCESSFUL)
171  return result;
172 
173  // Login must be before getpeername otherwise dante won't work.
174  Owner->Status(_("Logging in"));
175  result = Login();
176  if (result != ResultState::SUCCESSFUL)
177  return result;
178 
179  // Get the remote server's address
180  PeerAddrLen = sizeof(PeerAddr);
181  if (getpeername(ServerFd->Fd(), (sockaddr *)&PeerAddr, &PeerAddrLen) != 0)
182  {
183  _error->Errno("getpeername", _("Unable to determine the peer name"));
185  }
186 
187  // Get the local machine's address
188  ServerAddrLen = sizeof(ServerAddr);
189  if (getsockname(ServerFd->Fd(), (sockaddr *)&ServerAddr, &ServerAddrLen) != 0)
190  {
191  _error->Errno("getsockname", _("Unable to determine the local name"));
193  }
194 
196 }
197  /*}}}*/
198 // FTPConn::Login - Login to the remote server /*{{{*/
199 // ---------------------------------------------------------------------
200 /* This performs both normal login and proxy login using a simples script
201  stored in the config file. */
203 {
204  unsigned int Tag;
205  string Msg;
206 
207  // Setup the variables needed for authentication
208  string User = "anonymous";
209  string Pass = "apt_get_ftp_2.1@debian.linux.user";
210 
211  // Fill in the user/pass
212  if (ServerName.User.empty() == false)
213  User = ServerName.User;
214  if (ServerName.Password.empty() == false)
215  Pass = ServerName.Password;
216 
217  // Perform simple login
218  if (Proxy.empty() == true)
219  {
220  // Read the initial response
221  if (ReadResp(Tag,Msg) == false)
223  if (Tag >= 400)
224  {
225  _error->Error(_("The server refused the connection and said: %s"), Msg.c_str());
227  }
228 
229  // Send the user
230  if (WriteMsg(Tag,Msg,"USER %s",User.c_str()) == false)
232  if (Tag >= 400)
233  {
234  _error->Error(_("USER failed, server said: %s"), Msg.c_str());
236  }
237 
238  if (Tag == 331) { // 331 User name okay, need password.
239  // Send the Password
240  if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false)
242  if (Tag >= 400)
243  {
244  _error->Error(_("PASS failed, server said: %s"), Msg.c_str());
246  }
247  }
248 
249  // Enter passive mode
250  if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
251  TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
252  else
253  TryPassive = _config->FindB("Acquire::FTP::Passive",true);
254  }
255  else
256  {
257  // Read the initial response
258  if (ReadResp(Tag,Msg) == false)
260  if (Tag >= 400)
261  {
262  _error->Error(_("The server refused the connection and said: %s"), Msg.c_str());
264  }
265 
266  // Perform proxy script execution
267  Configuration::Item const *Opts = _config->Tree("Acquire::ftp::ProxyLogin");
268  if (Opts == 0 || Opts->Child == 0)
269  {
270  _error->Error(_("A proxy server was specified but no login "
271  "script, Acquire::ftp::ProxyLogin is empty."));
273  }
274  Opts = Opts->Child;
275 
276  // Iterate over the entire login script
277  for (; Opts != 0; Opts = Opts->Next)
278  {
279  if (Opts->Value.empty() == true)
280  continue;
281 
282  // Substitute the variables into the command
283  string Tmp = Opts->Value;
284  Tmp = SubstVar(Tmp,"$(PROXY_USER)",Proxy.User);
285  Tmp = SubstVar(Tmp,"$(PROXY_PASS)",Proxy.Password);
286  Tmp = SubstVar(Tmp,"$(SITE_USER)",User);
287  Tmp = SubstVar(Tmp,"$(SITE_PASS)",Pass);
288  if (ServerName.Port != 0)
289  {
290  std::string SitePort;
291  strprintf(SitePort, "%u", ServerName.Port);
292  Tmp = SubstVar(Tmp,"$(SITE_PORT)", SitePort);
293  }
294  else
295  Tmp = SubstVar(Tmp,"$(SITE_PORT)", "21");
296  Tmp = SubstVar(Tmp,"$(SITE)",ServerName.Host);
297 
298  // Send the command
299  if (WriteMsg(Tag,Msg,"%s",Tmp.c_str()) == false)
301  if (Tag >= 400)
302  {
303  _error->Error(_("Login script command '%s' failed, server said: %s"), Tmp.c_str(), Msg.c_str());
305  }
306  }
307 
308  // Enter passive mode
309  TryPassive = false;
310  if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
311  TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
312  else
313  {
314  if (_config->Exists("Acquire::FTP::Proxy::Passive") == true)
315  TryPassive = _config->FindB("Acquire::FTP::Proxy::Passive",true);
316  else
317  TryPassive = _config->FindB("Acquire::FTP::Passive",true);
318  }
319  }
320 
321  // Force the use of extended commands
322  if (_config->Exists("Acquire::FTP::ForceExtended::" + ServerName.Host) == true)
323  ForceExtended = _config->FindB("Acquire::FTP::ForceExtended::" + ServerName.Host,true);
324  else
325  ForceExtended = _config->FindB("Acquire::FTP::ForceExtended",false);
326 
327  // Binary mode
328  if (WriteMsg(Tag,Msg,"TYPE I") == false)
330  if (Tag >= 400)
331  {
332  _error->Error(_("TYPE failed, server said: %s"), Msg.c_str());
334  }
336 }
337  /*}}}*/
338 // FTPConn::ReadLine - Read a line from the server /*{{{*/
339 // ---------------------------------------------------------------------
340 /* This performs a very simple buffered read. */
341 bool FTPConn::ReadLine(string &Text)
342 {
343  if (ServerFd->Fd() == -1)
344  return false;
345 
346  // Suck in a line
347  while (Len < sizeof(Buffer))
348  {
349  // Scan the buffer for a new line
350  for (unsigned int I = 0; I != Len; I++)
351  {
352  // Escape some special chars
353  if (Buffer[I] == 0)
354  Buffer[I] = '?';
355 
356  // End of line?
357  if (Buffer[I] != '\n')
358  continue;
359 
360  I++;
361  Text = string(Buffer,I);
362  memmove(Buffer,Buffer+I,Len - I);
363  Len -= I;
364  return true;
365  }
366 
367  // Wait for some data..
368  if (WaitFd(ServerFd->Fd(), false, TimeOut) == false)
369  {
370  Close();
371  return _error->Error(_("Connection timeout"));
372  }
373 
374  // Suck it back
375  int Res = ServerFd->Read(Buffer + Len, sizeof(Buffer) - Len);
376  if (Res == 0)
377  _error->Error(_("Server closed the connection"));
378  if (Res <= 0)
379  {
380  _error->Errno("read",_("Read error"));
381  Close();
382  return false;
383  }
384  Len += Res;
385  }
386 
387  return _error->Error(_("A response overflowed the buffer."));
388 }
389  /*}}}*/
390 // FTPConn::ReadResp - Read a full response from the server /*{{{*/
391 // ---------------------------------------------------------------------
392 /* This reads a reply code from the server, it handles both p */
393 bool FTPConn::ReadResp(unsigned int &Ret,string &Text)
394 {
395  // Grab the first line of the response
396  string Msg;
397  if (ReadLine(Msg) == false)
398  return false;
399 
400  // Get the ID code
401  char *End;
402  Ret = strtol(Msg.c_str(),&End,10);
403  if (End - Msg.c_str() != 3)
404  return _error->Error(_("Protocol corruption"));
405 
406  // All done ?
407  Text = Msg.c_str()+4;
408  if (*End == ' ')
409  {
410  if (Debug == true)
411  cerr << "<- '" << QuoteString(Text,"") << "'" << endl;
412  return true;
413  }
414 
415  if (*End != '-')
416  return _error->Error(_("Protocol corruption"));
417 
418  /* Okay, here we do the continued message trick. This is foolish, but
419  proftpd follows the protocol as specified and wu-ftpd doesn't, so
420  we filter. I wonder how many clients break if you use proftpd and
421  put a '- in the 3rd spot in the message? */
422  char Leader[4];
423  strncpy(Leader,Msg.c_str(),3);
424  Leader[3] = 0;
425  while (ReadLine(Msg) == true)
426  {
427  // Short, it must be using RFC continuation..
428  if (Msg.length() < 4)
429  {
430  Text += Msg;
431  continue;
432  }
433 
434  // Oops, finished
435  if (strncmp(Msg.c_str(),Leader,3) == 0 && Msg[3] == ' ')
436  {
437  Text += Msg.c_str()+4;
438  break;
439  }
440 
441  // This message has the wu-ftpd style reply code prefixed
442  if (strncmp(Msg.c_str(),Leader,3) == 0 && Msg[3] == '-')
443  {
444  Text += Msg.c_str()+4;
445  continue;
446  }
447 
448  // Must be RFC style prefixing
449  Text += Msg;
450  }
451 
452  if (Debug == true && _error->PendingError() == false)
453  cerr << "<- '" << QuoteString(Text,"") << "'" << endl;
454 
455  return !_error->PendingError();
456 }
457  /*}}}*/
458 // FTPConn::WriteMsg - Send a message to the server /*{{{*/
459 // ---------------------------------------------------------------------
460 /* Simple printf like function.. */
461 bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
462 {
463  va_list args;
464  va_start(args,Fmt);
465 
466  // sprintf the description
467  char S[400];
468  vsnprintf(S,sizeof(S) - 4,Fmt,args);
469  strcat(S,"\r\n");
470  va_end(args);
471 
472  if (Debug == true)
473  cerr << "-> '" << QuoteString(S,"") << "'" << endl;
474 
475  // Send it off
476  unsigned long Len = strlen(S);
477  unsigned long Start = 0;
478  while (Len != 0)
479  {
480  if (WaitFd(ServerFd->Fd(), true, TimeOut) == false)
481  {
482  Close();
483  return _error->Error(_("Connection timeout"));
484  }
485 
486  int Res = ServerFd->Write(S + Start, Len);
487  if (Res <= 0)
488  {
489  _error->Errno("write",_("Write error"));
490  Close();
491  return false;
492  }
493 
494  Len -= Res;
495  Start += Res;
496  }
497 
498  return ReadResp(Ret,Text);
499 }
500  /*}}}*/
501 // FTPConn::GoPasv - Enter Passive mode /*{{{*/
502 // ---------------------------------------------------------------------
503 /* Try to enter passive mode, the return code does not indicate if passive
504  mode could or could not be established, only if there was a fatal error.
505  We have to enter passive mode every time we make a data connection :| */
507 {
508  /* The PASV command only works on IPv4 sockets, even though it could
509  in theory suppory IPv6 via an all zeros reply */
510  if (((struct sockaddr *)&PeerAddr)->sa_family != AF_INET ||
511  ForceExtended == true)
512  return ExtGoPasv();
513 
514  if (PasvAddr != 0)
516  PasvAddr = 0;
517 
518  // Try to enable pasv mode
519  unsigned int Tag;
520  string Msg;
521  if (WriteMsg(Tag,Msg,"PASV") == false)
522  return false;
523 
524  // Unsupported function
525  string::size_type Pos = Msg.find('(');
526  if (Tag >= 400)
527  return true;
528 
529  //wu-2.6.2(1) ftp server, returns
530  //227 Entering Passive Mode 193,219,28,140,150,111
531  //without parentheses, let's try to cope with it.
532  //wget(1) and ftp(1) can.
533  if (Pos == string::npos)
534  Pos = Msg.rfind(' ');
535  else
536  ++Pos;
537 
538  // Still unsupported function
539  if (Pos == string::npos)
540  return true;
541 
542  // Scan it
543  unsigned a0,a1,a2,a3,p0,p1;
544  if (sscanf(Msg.c_str() + Pos,"%u,%u,%u,%u,%u,%u",&a0,&a1,&a2,&a3,&p0,&p1) != 6)
545  return true;
546 
547  /* Some evil servers return 0 to mean their addr. We can actually speak
548  to these servers natively using IPv6 */
549  if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
550  {
551  // Get the IP in text form
552  char Name[NI_MAXHOST];
553  char Service[NI_MAXSERV];
554  getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
555  Name,sizeof(Name),Service,sizeof(Service),
557 
558  struct addrinfo Hints;
559  memset(&Hints,0,sizeof(Hints));
560  Hints.ai_socktype = SOCK_STREAM;
561  Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
562  Hints.ai_flags |= AI_NUMERICHOST;
563 
564  // Get a new passive address.
565  char Port[100];
566  snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
567  if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
568  return true;
569  return true;
570  }
571 
572  struct addrinfo Hints;
573  memset(&Hints,0,sizeof(Hints));
574  Hints.ai_socktype = SOCK_STREAM;
575  Hints.ai_family = AF_INET;
576  Hints.ai_flags |= AI_NUMERICHOST;
577 
578  // Get a new passive address.
579  char Port[100];
580  snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
581  char Name[100];
582  snprintf(Name,sizeof(Name),"%u.%u.%u.%u",a0,a1,a2,a3);
583  if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
584  return true;
585  return true;
586 }
587  /*}}}*/
588 // FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
589 // ---------------------------------------------------------------------
590 /* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
592 {
593  if (PasvAddr != 0)
595  PasvAddr = 0;
596 
597  // Try to enable pasv mode
598  unsigned int Tag;
599  string Msg;
600  if (WriteMsg(Tag,Msg,"EPSV") == false)
601  return false;
602 
603  // Unsupported function
604  string::size_type Pos = Msg.find('(');
605  if (Tag >= 400 || Pos == string::npos)
606  return true;
607 
608  // Scan it
609  string::const_iterator List[4];
610  unsigned Count = 0;
611  Pos++;
612  for (string::const_iterator I = Msg.begin() + Pos; I < Msg.end(); ++I)
613  {
614  if (*I != Msg[Pos])
615  continue;
616  if (Count >= 4)
617  return true;
618  List[Count++] = I;
619  }
620  if (Count != 4)
621  return true;
622 
623  // Break it up ..
624  unsigned long Proto = 0;
625  unsigned long Port = 0;
626  string IP;
627  IP = string(List[1]+1,List[2]);
628  Port = atoi(string(List[2]+1,List[3]).c_str());
629  if (IP.empty() == false)
630  Proto = atoi(string(List[0]+1,List[1]).c_str());
631 
632  if (Port == 0)
633  return false;
634 
635  // String version of the port
636  char PStr[100];
637  snprintf(PStr,sizeof(PStr),"%lu",Port);
638 
639  // Get the IP in text form
640  struct addrinfo Hints;
641  memset(&Hints,0,sizeof(Hints));
642  Hints.ai_socktype = SOCK_STREAM;
643  Hints.ai_flags |= AI_NUMERICHOST;
644 
645  /* The RFC defined case, connect to the old IP/protocol using the
646  new port. */
647  if (IP.empty() == true)
648  {
649  // Get the IP in text form
650  char Name[NI_MAXHOST];
651  char Service[NI_MAXSERV];
652  getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
653  Name,sizeof(Name),Service,sizeof(Service),
655  IP = Name;
656  Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
657  }
658  else
659  {
660  // Get the family..
661  Hints.ai_family = 0;
662  for (unsigned J = 0; AFMap[J].Family != 0; J++)
663  if (AFMap[J].IETFFamily == Proto)
664  Hints.ai_family = AFMap[J].Family;
665  if (Hints.ai_family == 0)
666  return true;
667  }
668 
669  // Get a new passive address.
670  if (getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr) != 0)
671  return true;
672 
673  return true;
674 }
675  /*}}}*/
676 // FTPConn::Size - Return the size of a file /*{{{*/
677 // ---------------------------------------------------------------------
678 /* Grab the file size from the server, 0 means no size or empty file */
679 bool FTPConn::Size(const char *Path,unsigned long long &Size)
680 {
681  // Query the size
682  unsigned int Tag;
683  string Msg;
684  Size = 0;
685  if (WriteMsg(Tag,Msg,"SIZE %s",Path) == false)
686  return false;
687 
688  char *End;
689  Size = strtoull(Msg.c_str(),&End,10);
690  if (Tag >= 400 || End == Msg.c_str())
691  Size = 0;
692  return true;
693 }
694  /*}}}*/
695 // FTPConn::ModTime - Return the modification time of the file /*{{{*/
696 // ---------------------------------------------------------------------
697 /* Like Size no error is returned if the command is not supported. If the
698  command fails then time is set to the current time of day to fool
699  date checks. */
700 bool FTPConn::ModTime(const char *Path, time_t &Time)
701 {
702  Time = time(&Time);
703 
704  // Query the mod time
705  unsigned int Tag;
706  string Msg;
707  if (WriteMsg(Tag,Msg,"MDTM %s",Path) == false)
708  return false;
709  if (Tag >= 400 || Msg.empty() == true || isdigit(Msg[0]) == 0)
710  return true;
711 
712  // Parse it
713  return FTPMDTMStrToTime(Msg.c_str(), Time);
714 }
715  /*}}}*/
716 // FTPConn::CreateDataFd - Get a data connection /*{{{*/
717 // ---------------------------------------------------------------------
718 /* Create the data connection. Call FinalizeDataFd after this though.. */
720 {
721  close(DataFd);
722  DataFd = -1;
723 
724  // Attempt to enter passive mode.
725  if (TryPassive == true)
726  {
727  if (GoPasv() == false)
728  return false;
729 
730  // Oops, didn't work out, don't bother trying again.
731  if (PasvAddr == 0)
732  TryPassive = false;
733  }
734 
735  // Passive mode?
736  if (PasvAddr != 0)
737  {
738  // Get a socket
739  if ((DataFd = socket(PasvAddr->ai_family,PasvAddr->ai_socktype,
740  PasvAddr->ai_protocol)) < 0)
741  return _error->Errno("socket",_("Could not create a socket"));
742 
743  // Connect to the server
744  SetNonBlock(DataFd,true);
745  if (connect(DataFd,PasvAddr->ai_addr,PasvAddr->ai_addrlen) < 0 &&
746  errno != EINPROGRESS)
747  return _error->Errno("socket",_("Could not create a socket"));
748 
749  /* This implements a timeout for connect by opening the connection
750  nonblocking */
751  if (WaitFd(DataFd,true,TimeOut) == false)
752  return _error->Error(_("Could not connect data socket, connection timed out"));
753  unsigned int Err;
754  unsigned int Len = sizeof(Err);
755  if (getsockopt(DataFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
756  return _error->Errno("getsockopt",_("Failed"));
757  if (Err != 0)
758  return _error->Error(_("Could not connect passive socket."));
759 
760  return true;
761  }
762 
763  // Port mode :<
764  close(DataListenFd);
765  DataListenFd = -1;
766 
767  // Get the information for a listening socket.
768  struct addrinfo *BindAddr = NULL;
769  struct addrinfo Hints;
770  memset(&Hints,0,sizeof(Hints));
771  Hints.ai_socktype = SOCK_STREAM;
772  Hints.ai_flags |= AI_PASSIVE;
773  Hints.ai_family = ((struct sockaddr *)&ServerAddr)->sa_family;
774  if (getaddrinfo(0,"0",&Hints,&BindAddr) != 0 || BindAddr == NULL)
775  return _error->Error(_("getaddrinfo was unable to get a listening socket"));
776 
777  // Construct the socket
778  if ((DataListenFd = socket(BindAddr->ai_family,BindAddr->ai_socktype,
779  BindAddr->ai_protocol)) < 0)
780  {
781  freeaddrinfo(BindAddr);
782  return _error->Errno("socket",_("Could not create a socket"));
783  }
784 
785  // Bind and listen
786  if (::bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0)
787  {
788  freeaddrinfo(BindAddr);
789  return _error->Errno("bind",_("Could not bind a socket"));
790  }
791  freeaddrinfo(BindAddr);
792  if (listen(DataListenFd,1) < 0)
793  return _error->Errno("listen",_("Could not listen on the socket"));
795 
796  // Determine the name to send to the remote
797  struct sockaddr_storage Addr;
798  socklen_t AddrLen = sizeof(Addr);
799  if (getsockname(DataListenFd,(sockaddr *)&Addr,&AddrLen) < 0)
800  return _error->Errno("getsockname",_("Could not determine the socket's name"));
801 
802 
803  // Reverse the address. We need the server address and the data port.
804  char Name[NI_MAXHOST];
805  char Service[NI_MAXSERV];
806  char Service2[NI_MAXSERV];
807  getnameinfo((struct sockaddr *)&Addr,AddrLen,
808  Name,sizeof(Name),Service,sizeof(Service),
810  getnameinfo((struct sockaddr *)&ServerAddr,ServerAddrLen,
811  Name,sizeof(Name),Service2,sizeof(Service2),
813 
814  // Send off an IPv4 address in the old port format
815  if (((struct sockaddr *)&Addr)->sa_family == AF_INET &&
816  ForceExtended == false)
817  {
818  // Convert the dots in the quad into commas
819  for (char *I = Name; *I != 0; I++)
820  if (*I == '.')
821  *I = ',';
822  unsigned long Port = atoi(Service);
823 
824  // Send the port command
825  unsigned int Tag;
826  string Msg;
827  if (WriteMsg(Tag,Msg,"PORT %s,%d,%d",
828  Name,
829  (int)(Port >> 8) & 0xff, (int)(Port & 0xff)) == false)
830  return false;
831  if (Tag >= 400)
832  return _error->Error(_("Unable to send PORT command"));
833  return true;
834  }
835 
836  // Construct an EPRT command
837  unsigned Proto = 0;
838  for (unsigned J = 0; AFMap[J].Family != 0; J++)
839  if (AFMap[J].Family == ((struct sockaddr *)&Addr)->sa_family)
840  Proto = AFMap[J].IETFFamily;
841  if (Proto == 0)
842  return _error->Error(_("Unknown address family %u (AF_*)"),
843  ((struct sockaddr *)&Addr)->sa_family);
844 
845  // Send the EPRT command
846  unsigned int Tag;
847  string Msg;
848  if (WriteMsg(Tag,Msg,"EPRT |%u|%s|%s|",Proto,Name,Service) == false)
849  return false;
850  if (Tag >= 400)
851  return _error->Error(_("EPRT failed, server said: %s"),Msg.c_str());
852  return true;
853 }
854  /*}}}*/
855 // FTPConn::Finalize - Complete the Data connection /*{{{*/
856 // ---------------------------------------------------------------------
857 /* If the connection is in port mode this waits for the other end to hook
858  up to us. */
860 {
861  // Passive mode? Do nothing
862  if (PasvAddr != 0)
863  return true;
864 
865  // Close any old socket..
866  close(DataFd);
867  DataFd = -1;
868 
869  // Wait for someone to connect..
870  if (WaitFd(DataListenFd,false,TimeOut) == false)
871  return _error->Error(_("Data socket connect timed out"));
872 
873  // Accept the connection
874  struct sockaddr_in Addr;
875  socklen_t Len = sizeof(Addr);
876  DataFd = accept(DataListenFd,(struct sockaddr *)&Addr,&Len);
877  if (DataFd < 0)
878  return _error->Errno("accept",_("Unable to accept connection"));
879 
880  close(DataListenFd);
881  DataListenFd = -1;
882 
883  return true;
884 }
885  /*}}}*/
886 // FTPConn::Get - Get a file /*{{{*/
887 // ---------------------------------------------------------------------
888 /* This opens a data connection, sends REST and RETR and then
889  transfers the file over. */
890 bool FTPConn::Get(const char *Path,FileFd &To,unsigned long long Resume,
891  Hashes &Hash,bool &Missing, unsigned long long MaximumSize,
892  pkgAcqMethod *Owner)
893 {
894  Missing = false;
895  if (CreateDataFd() == false)
896  return false;
897 
898  unsigned int Tag;
899  string Msg;
900  if (Resume != 0)
901  {
902  if (WriteMsg(Tag,Msg,"REST %u",Resume) == false)
903  return false;
904  if (Tag >= 400)
905  Resume = 0;
906  }
907 
908  if (To.Truncate(Resume) == false)
909  return false;
910 
911  if (To.Seek(0) == false)
912  return false;
913 
914  if (Resume != 0)
915  {
916  if (Hash.AddFD(To,Resume) == false)
917  {
918  _error->Errno("read",_("Problem hashing file"));
919  return false;
920  }
921  }
922 
923  // Send the get command
924  if (WriteMsg(Tag,Msg,"RETR %s",Path) == false)
925  return false;
926 
927  if (Tag >= 400)
928  {
929  if (Tag == 550)
930  Missing = true;
931  return _error->Error(_("Unable to fetch file, server said '%s'"),Msg.c_str());
932  }
933 
934  // Finish off the data connection
935  if (Finalize() == false)
936  return false;
937 
938  // Copy loop
939  unsigned char Buffer[4096];
940  while (1)
941  {
942  // Wait for some data..
943  if (WaitFd(DataFd,false,TimeOut) == false)
944  {
945  Close();
946  return _error->Error(_("Data socket timed out"));
947  }
948 
949  // Read the data..
950  int Res = read(DataFd,Buffer,sizeof(Buffer));
951  if (Res == 0)
952  break;
953  if (Res < 0)
954  {
955  if (errno == EAGAIN)
956  continue;
957  break;
958  }
959 
960  Hash.Add(Buffer,Res);
961  if (To.Write(Buffer,Res) == false)
962  {
963  Close();
964  return false;
965  }
966 
967  if (MaximumSize > 0 && To.Tell() > MaximumSize)
968  {
969  Owner->SetFailReason("MaximumSizeExceeded");
970  return _error->Error(_("File has unexpected size (%llu != %llu). Mirror sync in progress?"),
971  To.Tell(), MaximumSize);
972  }
973  }
974 
975  // All done
976  close(DataFd);
977  DataFd = -1;
978 
979  // Read the closing message from the server
980  if (ReadResp(Tag,Msg) == false)
981  return false;
982  if (Tag >= 400)
983  return _error->Error(_("Data transfer failed, server said '%s'"),Msg.c_str());
984  return true;
985 }
986  /*}}}*/
987 
988 // FtpMethod::FtpMethod - Constructor /*{{{*/
989 // ---------------------------------------------------------------------
990 /* */
991 FtpMethod::FtpMethod() : aptAuthConfMethod("ftp", "1.0", SendConfig | SendURIEncoded)
992 {
994  signal(SIGTERM,SigTerm);
995  signal(SIGINT,SigTerm);
996 
997  Server = 0;
998  FailFd = -1;
999 }
1000  /*}}}*/
1001 // FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
1002 // ---------------------------------------------------------------------
1003 /* This closes and timestamps the open file. This is necessary to get
1004  resume behavior on user abort */
1006 {
1007  if (FailFd == -1)
1008  _exit(100);
1009 
1010  // Timestamp
1011  struct timeval times[2];
1012  times[0].tv_sec = FailTime;
1013  times[1].tv_sec = FailTime;
1014  times[0].tv_usec = times[1].tv_usec = 0;
1015  utimes(FailFile.c_str(), times);
1016 
1017  close(FailFd);
1018 
1019  _exit(100);
1020 }
1021  /*}}}*/
1022 // FtpMethod::Configuration - Handle a configuration message /*{{{*/
1023 // ---------------------------------------------------------------------
1024 /* We stash the desired pipeline depth */
1025 bool FtpMethod::Configuration(string Message)
1026 {
1027  if (aptAuthConfMethod::Configuration(Message) == false)
1028  return false;
1029 
1030  TimeOut = _config->FindI("Acquire::Ftp::Timeout",TimeOut);
1031 
1032  return true;
1033 }
1034  /*}}}*/
1035 // FtpMethod::Fetch - Fetch a file /*{{{*/
1036 // ---------------------------------------------------------------------
1037 /* Fetch a single file, called by the base class.. */
1039 {
1040  URI Get(Itm->Uri);
1041  auto const File = DecodeSendURI(Get.Path);
1042  FetchResult Res;
1043  Res.Filename = Itm->DestFile;
1044  Res.IMSHit = false;
1045 
1046  MaybeAddAuthTo(Get);
1047 
1048  // Connect to the server
1049  if (Server == 0 || Server->Comp(Get) == false)
1050  {
1051  delete Server;
1052  Server = new FTPConn(Get);
1053  }
1054 
1055  // Could not connect is a transient error..
1056  switch (Server->Open(this))
1057  {
1059  Server->Close();
1060  Fail(true);
1061  return true;
1063  Server->Close();
1064  Fail(false);
1065  return true;
1067  break;
1068  }
1069 
1070  // Get the files information
1071  Status(_("Query"));
1072  unsigned long long Size;
1073  if (not Server->Size(File.c_str(), Size) ||
1074  not Server->ModTime(File.c_str(), FailTime))
1075  {
1076  Fail(true);
1077  return true;
1078  }
1079  Res.Size = Size;
1080 
1081  // See if it is an IMS hit
1082  if (Itm->LastModified == FailTime)
1083  {
1084  Res.Size = 0;
1085  Res.IMSHit = true;
1086  URIDone(Res);
1087  return true;
1088  }
1089 
1090  // See if the file exists
1091  struct stat Buf;
1092  if (stat(Itm->DestFile.c_str(),&Buf) == 0)
1093  {
1094  if (Size == (unsigned long long)Buf.st_size && FailTime == Buf.st_mtime)
1095  {
1096  Res.Size = Buf.st_size;
1097  Res.LastModified = Buf.st_mtime;
1098  Res.ResumePoint = Buf.st_size;
1099  URIDone(Res);
1100  return true;
1101  }
1102 
1103  // Resume?
1104  if (FailTime == Buf.st_mtime && Size > (unsigned long long)Buf.st_size)
1105  Res.ResumePoint = Buf.st_size;
1106  }
1107 
1108  // Open the file
1109  Hashes Hash(Itm->ExpectedHashes);
1110  {
1111  FileFd Fd(Itm->DestFile,FileFd::WriteAny);
1112  if (_error->PendingError() == true)
1113  return false;
1114 
1115  URIStart(Res);
1116 
1117  FailFile = Itm->DestFile;
1118  FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
1119  FailFd = Fd.Fd();
1120 
1121  bool Missing;
1122  if (not Server->Get(File.c_str(), Fd, Res.ResumePoint, Hash, Missing, Itm->MaximumSize, this))
1123  {
1124  Fd.Close();
1125 
1126  // Timestamp
1127  struct timeval times[2];
1128  times[0].tv_sec = FailTime;
1129  times[1].tv_sec = FailTime;
1130  times[0].tv_usec = times[1].tv_usec = 0;
1131  utimes(FailFile.c_str(), times);
1132 
1133  // If the file is missing we hard fail and delete the destfile
1134  // otherwise transient fail
1135  if (Missing == true) {
1136  RemoveFile("ftp", FailFile);
1137  return false;
1138  }
1139  Fail(true);
1140  return true;
1141  }
1142 
1143  Res.Size = Fd.Size();
1144 
1145  // Timestamp
1146  struct timeval times[2];
1147  times[0].tv_sec = FailTime;
1148  times[1].tv_sec = FailTime;
1149  times[0].tv_usec = times[1].tv_usec = 0;
1150  utimes(Fd.Name().c_str(), times);
1151  FailFd = -1;
1152  }
1153 
1154  Res.LastModified = FailTime;
1155  Res.TakeHashes(Hash);
1156 
1157  URIDone(Res);
1158 
1159  return true;
1160 }
1161  /*}}}*/
1162 
1163 int main(int, const char *argv[])
1164 {
1165  /* See if we should be come the http client - we do this for http
1166  proxy urls */
1167  if (getenv("ftp_proxy") != 0)
1168  {
1169  URI Proxy(string(getenv("ftp_proxy")));
1170 
1171  // Run the HTTP method
1172  if (Proxy.Access == "http")
1173  {
1174  // Copy over the environment setting
1175  char S[300];
1176  snprintf(S,sizeof(S),"http_proxy=%s",getenv("ftp_proxy"));
1177  putenv(S);
1178  putenv((char *)"no_proxy=");
1179 
1180  // Run the http method
1181  string Path = flNotFile(argv[0]) + "http";
1182  execl(Path.c_str(),Path.c_str(),(char *)NULL);
1183  cerr << _("Unable to invoke ") << Path << endl;
1184  exit(100);
1185  }
1186  }
1187  return FtpMethod().Run();
1188 }
strprintf(m, msg, repo.c_str())
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
return false
ResultState
Definition: aptmethod.h:35
Definition: ftp.h:22
struct sockaddr_storage ServerAddr
Definition: ftp.h:40
ResultState Open(aptMethod *Owner)
Definition: ftp.cc:112
socklen_t PeerAddrLen
Definition: ftp.h:37
bool Comp(URI Other)
Definition: ftp.h:51
bool Finalize()
Definition: ftp.cc:859
~FTPConn()
Definition: ftp.cc:87
bool ReadLine(std::string &Text)
Definition: ftp.cc:341
bool GoPasv()
Definition: ftp.cc:506
URI ServerName
Definition: ftp.h:28
ResultState Login()
Definition: ftp.cc:202
std::unique_ptr< MethodFd > ServerFd
Definition: ftp.h:25
bool Get(const char *Path, FileFd &To, unsigned long long Resume, Hashes &MD5, bool &Missing, unsigned long long MaximumSize, pkgAcqMethod *Owner)
Definition: ftp.cc:890
bool TryPassive
Definition: ftp.h:30
bool ReadResp(unsigned int &Ret, std::string &Text)
Definition: ftp.cc:393
bool ExtGoPasv()
Definition: ftp.cc:591
void Close()
Definition: ftp.cc:95
bool Size(const char *Path, unsigned long long &Size)
Definition: ftp.cc:679
bool CreateDataFd()
Definition: ftp.cc:719
FTPConn(URI Srv)
Definition: ftp.cc:74
unsigned long Len
Definition: ftp.h:24
struct sockaddr_storage PeerAddr
Definition: ftp.h:36
int DataListenFd
Definition: ftp.h:27
int DataFd
Definition: ftp.h:26
struct addrinfo * PasvAddr
Definition: ftp.h:33
bool ForceExtended
Definition: ftp.h:29
char Buffer[1024 *10]
Definition: ftp.h:23
socklen_t ServerAddrLen
Definition: ftp.h:41
bool WriteMsg(unsigned int &Ret, std::string &Text, const char *Fmt,...)
Definition: ftp.cc:461
bool Debug
Definition: ftp.h:31
bool ModTime(const char *Path, time_t &Time)
Definition: ftp.cc:700
Definition: fileutl.h:39
@ WriteAny
Definition: fileutl.h:71
bool Write(const void *From, unsigned long long Size)
Definition: fileutl.cc:2819
unsigned long long Tell()
Definition: fileutl.cc:2905
bool Seek(unsigned long long To)
Definition: fileutl.cc:2875
bool Truncate(unsigned long long To)
Definition: fileutl.cc:2892
int Fd()
Definition: fileutl.h:147
unsigned long long Size()
Definition: fileutl.cc:2967
bool Close()
Definition: fileutl.cc:2977
std::string & Name()
Definition: fileutl.h:156
Definition: ftp.h:75
virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE
Definition: ftp.cc:1038
FtpMethod()
Definition: ftp.cc:991
static time_t FailTime
Definition: ftp.h:83
static APT_NORETURN void SigTerm(int)
Definition: ftp.cc:1005
FTPConn * Server
Definition: ftp.h:79
virtual bool Configuration(std::string Message) APT_OVERRIDE
Definition: ftp.cc:1025
static int FailFd
Definition: ftp.h:82
static std::string FailFile
Definition: ftp.h:81
Definition: hashes.h:170
bool Add(const unsigned char *const Data, unsigned long long const Size) APT_NONNULL(2)
Definition: hashes.cc:353
bool AddFD(int const Fd, unsigned long long Size=0)
Definition: hashes.cc:362
Definition: strutl.h:193
std::string Access
Definition: strutl.h:198
std::string Path
Definition: strutl.h:202
bool empty()
Definition: strutl.h:207
unsigned int Port
Definition: strutl.h:203
std::string User
Definition: strutl.h:199
std::string Host
Definition: strutl.h:201
std::string Password
Definition: strutl.h:200
virtual bool Configuration(std::string Message) APT_OVERRIDE
Definition: aptmethod.h:515
bool MaybeAddAuthTo(URI &uri)
Definition: aptmethod.h:551
unsigned long SeccompFlags
Definition: aptmethod.h:50
struct timeval times[2]
Definition: aptmethod.h:469
static std::string DecodeSendURI(std::string const &part)
Definition: aptmethod.h:493
struct stat Buf
Definition: aptmethod.h:464
void Status(const char *Format,...)
void SetFailReason(std::string Msg)
virtual void URIStart(FetchResult &Res)
int Run(bool Single=false)
void Fail(bool Transient=false)
virtual void URIDone(FetchResult &Res, FetchResult *Alt=0)
Configuration * _config
void RotateDNS()
Definition: connect.cc:61
ResultState Connect(std::string Host, int Port, const char *Service, int DefPort, std::unique_ptr< MethodFd > &Fd, unsigned long TimeOut, aptMethod *Owner)
Definition: connect.cc:476
string flNotFile(string File)
Definition: fileutl.cc:676
void SetNonBlock(int Fd, bool Block)
Definition: fileutl.cc:804
bool WaitFd(int Fd, bool write, unsigned long timeout)
Definition: fileutl.cc:819
bool RemoveFile(char const *const Function, std::string const &FileName)
Definition: fileutl.cc:198
int main(int, const char *argv[])
Definition: ftp.cc:1163
unsigned long TimeOut
Definition: ftp.cc:65
URI Proxy
Definition: ftp.cc:66
int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo_emu *hints, struct addrinfo_emu **res)
Definition: rfc2553emu.cc:29
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
Definition: rfc2553emu.cc:170
void freeaddrinfo(struct addrinfo_emu *ai)
Definition: rfc2553emu.cc:154
#define AI_NUMERICHOST
Definition: rfc2553emu.h:109
#define NI_NUMERICHOST
Definition: rfc2553emu.h:97
#define sockaddr_storage
Definition: rfc2553emu.h:104
#define NI_MAXHOST
Definition: rfc2553emu.h:92
#define AI_PASSIVE
Definition: rfc2553emu.h:60
#define NI_MAXSERV
Definition: rfc2553emu.h:93
#define addrinfo
Definition: rfc2553emu.h:52
#define NI_NUMERICSERV
Definition: rfc2553emu.h:98
Definition: ftp.cc:54
unsigned long Family
Definition: ftp.cc:55
unsigned long IETFFamily
Definition: ftp.cc:56
Small representation of a file descriptor for network traffic.
Definition: connect.h:24
HashStringList ExpectedHashes
unsigned long long MaximumSize
unsigned long long Size
void TakeHashes(class Hashes &Hash)
unsigned long long ResumePoint
string SubstVar(const string &Str, const string &Subst, const string &Contents)
Definition: strutl.cc:502
bool CheckDomainList(const string &Host, const string &List)
Definition: strutl.cc:1527
string QuoteString(const string &Str, const char *Bad)
Definition: strutl.cc:384
bool FTPMDTMStrToTime(const char *const str, time_t &time)
Definition: strutl.cc:1131