// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: ftp.cc,v 1.12 1999/05/28 07:04:45 jgg Exp $
+// $Id: ftp.cc,v 1.31.2.1 2004/01/16 18:58:50 mdz Exp $
/* ######################################################################
- HTTP Aquire Method - This is the FTP aquire method for APT.
+ FTP Acquire Method - This is the FTP acquire method for APT.
This is a very simple implementation that does not try to optimize
at all. Commands are sent syncronously with the FTP server (as the
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
+#include <config.h>
+
#include <apt-pkg/fileutl.h>
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/error.h>
-#include <apt-pkg/md5.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/netrc.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/strutl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <utime.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
+#include <iostream>
// Internet stuff
#include <netinet/in.h>
-#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "rfc2553emu.h"
+#include "connect.h"
#include "ftp.h"
+
+#include <apti18n.h>
/*}}}*/
+using namespace std;
+
+/* This table is for the EPRT and EPSV commands, it maps the OS address
+ family to the IETF address families */
+struct AFMap
+{
+ unsigned long Family;
+ unsigned long IETFFamily;
+};
+
+#ifndef AF_INET6
+struct AFMap AFMap[] = {{AF_INET,1},{0, 0}};
+#else
+struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{0, 0}};
+#endif
+
unsigned long TimeOut = 120;
URI Proxy;
string FtpMethod::FailFile;
// FTPConn::FTPConn - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
-FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1),
- DataListenFd(-1), ServerName(Srv)
+FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1),
+ DataListenFd(-1), ServerName(Srv),
+ ForceExtended(false), TryPassive(true),
+ PeerAddrLen(0), ServerAddrLen(0)
{
Debug = _config->FindB("Debug::Acquire::Ftp",false);
- memset(&PasvAddr,0,sizeof(PasvAddr));
+ PasvAddr = 0;
+ Buffer[0] = '\0';
}
/*}}}*/
// FTPConn::~FTPConn - Destructor /*{{{*/
DataFd = -1;
close(DataListenFd);
DataListenFd = -1;
- memset(&PasvAddr,0,sizeof(PasvAddr));
+
+ if (PasvAddr != 0)
+ freeaddrinfo(PasvAddr);
+ PasvAddr = 0;
}
/*}}}*/
// FTPConn::Open - Open a new connection /*{{{*/
// ---------------------------------------------------------------------
/* Connect to the server using a non-blocking connection and perform a
login. */
-string LastHost;
-int LastPort = 0;
-struct addrinfo *LastHostAddr = 0;
bool FTPConn::Open(pkgAcqMethod *Owner)
{
// Use the already open connection if possible.
return true;
Close();
-
+
// Determine the proxy setting
- if (getenv("ftp_proxy") == 0)
+ string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host);
+ if (!SpecificProxy.empty())
{
- string DefProxy = _config->Find("Acquire::ftp::Proxy");
- string SpecificProxy = _config->Find("Acquire::ftp::Proxy::" + ServerName.Host);
- if (SpecificProxy.empty() == false)
- {
- if (SpecificProxy == "DIRECT")
- Proxy = "";
- else
- Proxy = SpecificProxy;
- }
- else
- Proxy = DefProxy;
+ if (SpecificProxy == "DIRECT")
+ Proxy = "";
+ else
+ Proxy = SpecificProxy;
}
else
- Proxy = getenv("ftp_proxy");
+ {
+ string DefProxy = _config->Find("Acquire::ftp::Proxy");
+ if (!DefProxy.empty())
+ {
+ Proxy = DefProxy;
+ }
+ else
+ {
+ char* result = getenv("ftp_proxy");
+ Proxy = result ? result : "";
+ }
+ }
+
+ // Parse no_proxy, a , separated list of domains
+ if (getenv("no_proxy") != 0)
+ {
+ if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
+ Proxy = "";
+ }
// Determine what host and port to use based on the proxy settings
int Port = 0;
Port = Proxy.Port;
Host = Proxy.Host;
}
-
- /* We used a cached address record.. Yes this is against the spec but
- the way we have setup our rotating dns suggests that this is more
- sensible */
- if (LastHost != Host || LastPort != Port)
- {
- Owner->Status("Connecting to %s",Host.c_str());
- // Lookup the host
- char S[30] = "ftp";
- if (Port != 0)
- snprintf(S,sizeof(S),"%u",Port);
-
- // Free the old address structure
- if (LastHostAddr != 0)
- {
- freeaddrinfo(LastHostAddr);
- LastHostAddr = 0;
- }
-
- // We only understand SOCK_STREAM sockets.
- struct addrinfo Hints;
- memset(&Hints,0,sizeof(Hints));
- Hints.ai_socktype = SOCK_STREAM;
-
- // Resolve both the host and service simultaneously
- if (getaddrinfo(Host.c_str(),S,&Hints,&LastHostAddr) != 0 ||
- LastHostAddr == 0)
- return _error->Error("Could not resolve '%s'",Host.c_str());
-
- LastHost = Host;
- LastPort = Port;
- }
+ /* Connect to the remote server. Since FTP is connection oriented we
+ want to make sure we get a new server every time we reconnect */
+ RotateDNS();
+ if (Connect(Host,Port,"ftp",21,ServerFd,TimeOut,Owner) == false)
+ return false;
- // Get the printable IP address
- char Name[NI_MAXHOST];
- Name[0] = 0;
- getnameinfo(LastHostAddr->ai_addr,LastHostAddr->ai_addrlen,
- Name,sizeof(Name),0,0,NI_NUMERICHOST);
- Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
-
- // Get a socket
- if ((ServerFd = socket(LastHostAddr->ai_family,LastHostAddr->ai_socktype,
- LastHostAddr->ai_protocol)) < 0)
- return _error->Errno("socket","Could not create a socket");
- SetNonBlock(ServerFd,true);
- if (connect(ServerFd,LastHostAddr->ai_addr,LastHostAddr->ai_addrlen) < 0 &&
- errno != EINPROGRESS)
- return _error->Errno("connect","Cannot initiate the connection "
- "to %s (%s).",Host.c_str(),Name);
- Peer = *((struct sockaddr_in *)LastHostAddr->ai_addr);
-
- /* This implements a timeout for connect by opening the connection
- nonblocking */
- if (WaitFd(ServerFd,true,TimeOut) == false)
- return _error->Error("Could not connect to %s (%s), "
- "connection timed out",Host.c_str(),Name);
- unsigned int Err;
- unsigned int Len = sizeof(Err);
- if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
- return _error->Errno("getsockopt","Failed");
- if (Err != 0)
- return _error->Error("Could not connect to %s (%s).",Host.c_str(),Name);
-
- Owner->Status("Logging in");
- return Login();
+ // Login must be before getpeername otherwise dante won't work.
+ Owner->Status(_("Logging in"));
+ bool Res = Login();
+
+ // Get the remote server's address
+ PeerAddrLen = sizeof(PeerAddr);
+ if (getpeername(ServerFd,(sockaddr *)&PeerAddr,&PeerAddrLen) != 0)
+ return _error->Errno("getpeername",_("Unable to determine the peer name"));
+
+ // Get the local machine's address
+ ServerAddrLen = sizeof(ServerAddr);
+ if (getsockname(ServerFd,(sockaddr *)&ServerAddr,&ServerAddrLen) != 0)
+ return _error->Errno("getsockname",_("Unable to determine the local name"));
+
+ return Res;
}
/*}}}*/
// FTPConn::Login - Login to the remote server /*{{{*/
// Setup the variables needed for authentication
string User = "anonymous";
- string Pass = "apt_get_ftp_2.0@debian.linux.user";
+ string Pass = "apt_get_ftp_2.1@debian.linux.user";
// Fill in the user/pass
if (ServerName.User.empty() == false)
if (ReadResp(Tag,Msg) == false)
return false;
if (Tag >= 400)
- return _error->Error("Server refused our connection and said: %s",Msg.c_str());
+ return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str());
// Send the user
if (WriteMsg(Tag,Msg,"USER %s",User.c_str()) == false)
return false;
if (Tag >= 400)
- return _error->Error("USER failed, server said: %s",Msg.c_str());
+ return _error->Error(_("USER failed, server said: %s"),Msg.c_str());
- // Send the Password
- if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false)
- return false;
- if (Tag >= 400)
- return _error->Error("PASS failed, server said: %s",Msg.c_str());
+ if (Tag == 331) { // 331 User name okay, need password.
+ // Send the Password
+ if (WriteMsg(Tag,Msg,"PASS %s",Pass.c_str()) == false)
+ return false;
+ if (Tag >= 400)
+ return _error->Error(_("PASS failed, server said: %s"),Msg.c_str());
+ }
// Enter passive mode
if (_config->Exists("Acquire::FTP::Passive::" + ServerName.Host) == true)
TryPassive = _config->FindB("Acquire::FTP::Passive::" + ServerName.Host,true);
else
- TryPassive = _config->FindB("Acquire::FTP::Passive",true);
+ TryPassive = _config->FindB("Acquire::FTP::Passive",true);
}
else
{
if (ReadResp(Tag,Msg) == false)
return false;
if (Tag >= 400)
- return _error->Error("Server refused our connection and said: %s",Msg.c_str());
+ return _error->Error(_("The server refused the connection and said: %s"),Msg.c_str());
// Perform proxy script execution
Configuration::Item const *Opts = _config->Tree("Acquire::ftp::ProxyLogin");
if (Opts == 0 || Opts->Child == 0)
- return _error->Error("A proxy server was specified but no login "
- "script, Acquire::ftp::ProxyLogin is empty.");
+ return _error->Error(_("A proxy server was specified but no login "
+ "script, Acquire::ftp::ProxyLogin is empty."));
Opts = Opts->Child;
// Iterate over the entire login script
{
if (Opts->Value.empty() == true)
continue;
-
+
// Substitute the variables into the command
- char SitePort[20];
- if (ServerName.Port != 0)
- sprintf(SitePort,"%u",ServerName.Port);
- else
- strcpy(SitePort,"21");
string Tmp = Opts->Value;
Tmp = SubstVar(Tmp,"$(PROXY_USER)",Proxy.User);
Tmp = SubstVar(Tmp,"$(PROXY_PASS)",Proxy.Password);
Tmp = SubstVar(Tmp,"$(SITE_USER)",User);
Tmp = SubstVar(Tmp,"$(SITE_PASS)",Pass);
- Tmp = SubstVar(Tmp,"$(SITE_PORT)",SitePort);
+ if (ServerName.Port != 0)
+ {
+ std::string SitePort;
+ strprintf(SitePort, "%u", ServerName.Port);
+ Tmp = SubstVar(Tmp,"$(SITE_PORT)", SitePort);
+ }
+ else
+ Tmp = SubstVar(Tmp,"$(SITE_PORT)", "21");
Tmp = SubstVar(Tmp,"$(SITE)",ServerName.Host);
// Send the command
if (WriteMsg(Tag,Msg,"%s",Tmp.c_str()) == false)
return false;
if (Tag >= 400)
- return _error->Error("Login script command '%s' failed, server said: %s",Tmp.c_str(),Msg.c_str());
+ return _error->Error(_("Login script command '%s' failed, server said: %s"),Tmp.c_str(),Msg.c_str());
}
// Enter passive mode
}
}
+ // Force the use of extended commands
+ if (_config->Exists("Acquire::FTP::ForceExtended::" + ServerName.Host) == true)
+ ForceExtended = _config->FindB("Acquire::FTP::ForceExtended::" + ServerName.Host,true);
+ else
+ ForceExtended = _config->FindB("Acquire::FTP::ForceExtended",false);
+
// Binary mode
if (WriteMsg(Tag,Msg,"TYPE I") == false)
return false;
if (Tag >= 400)
- return _error->Error("TYPE failed, server said: %s",Msg.c_str());
+ return _error->Error(_("TYPE failed, server said: %s"),Msg.c_str());
return true;
}
if (WaitFd(ServerFd,false,TimeOut) == false)
{
Close();
- return _error->Error("Connection timeout");
+ return _error->Error(_("Connection timeout"));
}
// Suck it back
int Res = read(ServerFd,Buffer + Len,sizeof(Buffer) - Len);
+ if (Res == 0)
+ _error->Error(_("Server closed the connection"));
if (Res <= 0)
{
+ _error->Errno("read",_("Read error"));
Close();
- return _error->Errno("read","Read error");
+ return false;
}
Len += Res;
}
- return _error->Error("A response overflowed the buffer.");
+ return _error->Error(_("A response overflowed the buffer."));
}
/*}}}*/
// FTPConn::ReadResp - Read a full response from the server /*{{{*/
char *End;
Ret = strtol(Msg.c_str(),&End,10);
if (End - Msg.c_str() != 3)
- return _error->Error("Protocol corruption");
+ return _error->Error(_("Protocol corruption"));
// All done ?
Text = Msg.c_str()+4;
}
if (*End != '-')
- return _error->Error("Protocol corruption");
+ return _error->Error(_("Protocol corruption"));
/* Okay, here we do the continued message trick. This is foolish, but
proftpd follows the protocol as specified and wu-ftpd doesn't, so
char S[400];
vsnprintf(S,sizeof(S) - 4,Fmt,args);
strcat(S,"\r\n");
+ va_end(args);
if (Debug == true)
cerr << "-> '" << QuoteString(S,"") << "'" << endl;
if (WaitFd(ServerFd,true,TimeOut) == false)
{
Close();
- return _error->Error("Connection timeout");
+ return _error->Error(_("Connection timeout"));
}
int Res = write(ServerFd,S + Start,Len);
if (Res <= 0)
{
+ _error->Errno("write",_("Write error"));
Close();
- return _error->Errno("write","Write Error");
+ return false;
}
Len -= Res;
// ---------------------------------------------------------------------
/* Try to enter passive mode, the return code does not indicate if passive
mode could or could not be established, only if there was a fatal error.
- Borrowed mostly from lftp. We have to enter passive mode every time
- we make a data connection :| */
+ We have to enter passive mode every time we make a data connection :| */
bool FTPConn::GoPasv()
{
+ /* The PASV command only works on IPv4 sockets, even though it could
+ in theory suppory IPv6 via an all zeros reply */
+ if (((struct sockaddr *)&PeerAddr)->sa_family != AF_INET ||
+ ForceExtended == true)
+ return ExtGoPasv();
+
+ if (PasvAddr != 0)
+ freeaddrinfo(PasvAddr);
+ PasvAddr = 0;
+
// Try to enable pasv mode
unsigned int Tag;
string Msg;
// Unsupported function
string::size_type Pos = Msg.find('(');
if (Tag >= 400 || Pos == string::npos)
- {
- memset(&PasvAddr,0,sizeof(PasvAddr));
return true;
- }
// Scan it
unsigned a0,a1,a2,a3,p0,p1;
if (sscanf(Msg.c_str() + Pos,"(%u,%u,%u,%u,%u,%u)",&a0,&a1,&a2,&a3,&p0,&p1) != 6)
+ return true;
+
+ /* Some evil servers return 0 to mean their addr. We can actually speak
+ to these servers natively using IPv6 */
+ if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
{
- memset(&PasvAddr,0,sizeof(PasvAddr));
+ // Get the IP in text form
+ char Name[NI_MAXHOST];
+ char Service[NI_MAXSERV];
+ getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
+ Name,sizeof(Name),Service,sizeof(Service),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+
+ struct addrinfo Hints;
+ memset(&Hints,0,sizeof(Hints));
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
+ Hints.ai_flags |= AI_NUMERICHOST;
+
+ // Get a new passive address.
+ char Port[100];
+ snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
+ if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
+ return true;
return true;
}
- // lftp used this horrid byte order manipulation.. Ik.
- PasvAddr.sin_family = AF_INET;
- unsigned char *a;
- unsigned char *p;
- a = (unsigned char *)&PasvAddr.sin_addr;
- p = (unsigned char *)&PasvAddr.sin_port;
+ struct addrinfo Hints;
+ memset(&Hints,0,sizeof(Hints));
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_family = AF_INET;
+ Hints.ai_flags |= AI_NUMERICHOST;
- // Some evil servers return 0 to mean their addr
- if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
+ // Get a new passive address.
+ char Port[100];
+ snprintf(Port,sizeof(Port),"%u",(p0 << 8) + p1);
+ char Name[100];
+ snprintf(Name,sizeof(Name),"%u.%u.%u.%u",a0,a1,a2,a3);
+ if (getaddrinfo(Name,Port,&Hints,&PasvAddr) != 0)
+ return true;
+ return true;
+}
+ /*}}}*/
+// FTPConn::ExtGoPasv - Enter Extended Passive mode /*{{{*/
+// ---------------------------------------------------------------------
+/* Try to enter extended passive mode. See GoPasv above and RFC 2428 */
+bool FTPConn::ExtGoPasv()
+{
+ if (PasvAddr != 0)
+ freeaddrinfo(PasvAddr);
+ PasvAddr = 0;
+
+ // Try to enable pasv mode
+ unsigned int Tag;
+ string Msg;
+ if (WriteMsg(Tag,Msg,"EPSV") == false)
+ return false;
+
+ // Unsupported function
+ string::size_type Pos = Msg.find('(');
+ if (Tag >= 400 || Pos == string::npos)
+ return true;
+
+ // Scan it
+ string::const_iterator List[4];
+ unsigned Count = 0;
+ Pos++;
+ for (string::const_iterator I = Msg.begin() + Pos; I < Msg.end(); ++I)
{
- PasvAddr.sin_addr = Peer.sin_addr;
+ if (*I != Msg[Pos])
+ continue;
+ if (Count >= 4)
+ return true;
+ List[Count++] = I;
+ }
+ if (Count != 4)
+ return true;
+
+ // Break it up ..
+ unsigned long Proto = 0;
+ unsigned long Port = 0;
+ string IP;
+ IP = string(List[1]+1,List[2]);
+ Port = atoi(string(List[2]+1,List[3]).c_str());
+ if (IP.empty() == false)
+ Proto = atoi(string(List[0]+1,List[1]).c_str());
+
+ if (Port == 0)
+ return false;
+
+ // String version of the port
+ char PStr[100];
+ snprintf(PStr,sizeof(PStr),"%lu",Port);
+
+ // Get the IP in text form
+ struct addrinfo Hints;
+ memset(&Hints,0,sizeof(Hints));
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags |= AI_NUMERICHOST;
+
+ /* The RFC defined case, connect to the old IP/protocol using the
+ new port. */
+ if (IP.empty() == true)
+ {
+ // Get the IP in text form
+ char Name[NI_MAXHOST];
+ char Service[NI_MAXSERV];
+ getnameinfo((struct sockaddr *)&PeerAddr,PeerAddrLen,
+ Name,sizeof(Name),Service,sizeof(Service),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ IP = Name;
+ Hints.ai_family = ((struct sockaddr *)&PeerAddr)->sa_family;
}
else
{
- a[0] = a0;
- a[1] = a1;
- a[2] = a2;
- a[3] = a3;
+ // Get the family..
+ Hints.ai_family = 0;
+ for (unsigned J = 0; AFMap[J].Family != 0; J++)
+ if (AFMap[J].IETFFamily == Proto)
+ Hints.ai_family = AFMap[J].Family;
+ if (Hints.ai_family == 0)
+ return true;
}
- p[0] = p0;
- p[1] = p1;
+ // Get a new passive address.
+ if (getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr) != 0)
+ return true;
return true;
}
// FTPConn::Size - Return the size of a file /*{{{*/
// ---------------------------------------------------------------------
/* Grab the file size from the server, 0 means no size or empty file */
-bool FTPConn::Size(const char *Path,unsigned long &Size)
+bool FTPConn::Size(const char *Path,unsigned long long &Size)
{
// Query the size
unsigned int Tag;
return false;
char *End;
- Size = strtol(Msg.c_str(),&End,10);
+ Size = strtoull(Msg.c_str(),&End,10);
if (Tag >= 400 || End == Msg.c_str())
Size = 0;
return true;
return true;
// Parse it
- struct tm tm;
- memset(&tm,0,sizeof(tm));
- if (sscanf(Msg.c_str(),"%4d%2d%2d%2d%2d%2d",&tm.tm_year,&tm.tm_mon,
- &tm.tm_mday,&tm.tm_hour,&tm.tm_min,&tm.tm_sec) != 6)
- return true;
-
- tm.tm_year -= 1900;
- tm.tm_mon--;
-
- /* We use timegm from the GNU C library, libapt-pkg will provide this
- symbol if it does not exist */
- Time = timegm(&tm);
- return true;
+ return FTPMDTMStrToTime(Msg.c_str(), Time);
}
/*}}}*/
// FTPConn::CreateDataFd - Get a data connection /*{{{*/
return false;
// Oops, didn't work out, don't bother trying again.
- if (PasvAddr.sin_port == 0)
+ if (PasvAddr == 0)
TryPassive = false;
}
// Passive mode?
- if (PasvAddr.sin_port != 0)
+ if (PasvAddr != 0)
{
// Get a socket
- if ((DataFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
- return _error->Errno("socket","Could not create a socket");
+ if ((DataFd = socket(PasvAddr->ai_family,PasvAddr->ai_socktype,
+ PasvAddr->ai_protocol)) < 0)
+ return _error->Errno("socket",_("Could not create a socket"));
// Connect to the server
SetNonBlock(DataFd,true);
- if (connect(DataFd,(sockaddr *)&PasvAddr,sizeof(PasvAddr)) < 0 &&
+ if (connect(DataFd,PasvAddr->ai_addr,PasvAddr->ai_addrlen) < 0 &&
errno != EINPROGRESS)
- return _error->Errno("socket","Could not create a socket");
+ return _error->Errno("socket",_("Could not create a socket"));
/* This implements a timeout for connect by opening the connection
nonblocking */
- if (WaitFd(ServerFd,true,TimeOut) == false)
- return _error->Error("Could not connect data socket, connection timed out");
+ if (WaitFd(DataFd,true,TimeOut) == false)
+ return _error->Error(_("Could not connect data socket, connection timed out"));
unsigned int Err;
unsigned int Len = sizeof(Err);
- if (getsockopt(ServerFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
- return _error->Errno("getsockopt","Failed");
+ if (getsockopt(DataFd,SOL_SOCKET,SO_ERROR,&Err,&Len) != 0)
+ return _error->Errno("getsockopt",_("Failed"));
if (Err != 0)
- return _error->Error("Could not connect.");
-
+ return _error->Error(_("Could not connect passive socket."));
+
return true;
}
close(DataListenFd);
DataListenFd = -1;
- // Get a socket
- if ((DataListenFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
- return _error->Errno("socket","Could not create a socket");
+ // Get the information for a listening socket.
+ struct addrinfo *BindAddr = NULL;
+ struct addrinfo Hints;
+ memset(&Hints,0,sizeof(Hints));
+ Hints.ai_socktype = SOCK_STREAM;
+ Hints.ai_flags |= AI_PASSIVE;
+ Hints.ai_family = ((struct sockaddr *)&ServerAddr)->sa_family;
+ if (getaddrinfo(0,"0",&Hints,&BindAddr) != 0 || BindAddr == NULL)
+ return _error->Error(_("getaddrinfo was unable to get a listening socket"));
+
+ // Construct the socket
+ if ((DataListenFd = socket(BindAddr->ai_family,BindAddr->ai_socktype,
+ BindAddr->ai_protocol)) < 0)
+ {
+ freeaddrinfo(BindAddr);
+ return _error->Errno("socket",_("Could not create a socket"));
+ }
// Bind and listen
- sockaddr_in Addr;
- memset(&Addr,0,sizeof(Addr));
- if (bind(DataListenFd,(sockaddr *)&Addr,sizeof(Addr)) < 0)
- return _error->Errno("bind","Could not bind a socket");
+ if (::bind(DataListenFd,BindAddr->ai_addr,BindAddr->ai_addrlen) < 0)
+ {
+ freeaddrinfo(BindAddr);
+ return _error->Errno("bind",_("Could not bind a socket"));
+ }
+ freeaddrinfo(BindAddr);
if (listen(DataListenFd,1) < 0)
- return _error->Errno("listen","Could not listen on the socket");
+ return _error->Errno("listen",_("Could not listen on the socket"));
SetNonBlock(DataListenFd,true);
// Determine the name to send to the remote
- sockaddr_in Addr2;
- socklen_t Jnk = sizeof(Addr);
- if (getsockname(DataListenFd,(sockaddr *)&Addr,&Jnk) < 0)
- return _error->Errno("getsockname","Could not determine the socket's name");
- Jnk = sizeof(Addr2);
- if (getsockname(ServerFd,(sockaddr *)&Addr2,&Jnk) < 0)
- return _error->Errno("getsockname","Could not determine the socket's name");
-
- // This bit ripped from qftp
- unsigned long badr = ntohl(*(unsigned long *)&Addr2.sin_addr);
- unsigned long bp = ntohs(Addr.sin_port);
-
- // Send the port command
+ struct sockaddr_storage Addr;
+ socklen_t AddrLen = sizeof(Addr);
+ if (getsockname(DataListenFd,(sockaddr *)&Addr,&AddrLen) < 0)
+ return _error->Errno("getsockname",_("Could not determine the socket's name"));
+
+
+ // Reverse the address. We need the server address and the data port.
+ char Name[NI_MAXHOST];
+ char Service[NI_MAXSERV];
+ char Service2[NI_MAXSERV];
+ getnameinfo((struct sockaddr *)&Addr,AddrLen,
+ Name,sizeof(Name),Service,sizeof(Service),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ getnameinfo((struct sockaddr *)&ServerAddr,ServerAddrLen,
+ Name,sizeof(Name),Service2,sizeof(Service2),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+
+ // Send off an IPv4 address in the old port format
+ if (((struct sockaddr *)&Addr)->sa_family == AF_INET &&
+ ForceExtended == false)
+ {
+ // Convert the dots in the quad into commas
+ for (char *I = Name; *I != 0; I++)
+ if (*I == '.')
+ *I = ',';
+ unsigned long Port = atoi(Service);
+
+ // Send the port command
+ unsigned int Tag;
+ string Msg;
+ if (WriteMsg(Tag,Msg,"PORT %s,%d,%d",
+ Name,
+ (int)(Port >> 8) & 0xff, (int)(Port & 0xff)) == false)
+ return false;
+ if (Tag >= 400)
+ return _error->Error(_("Unable to send PORT command"));
+ return true;
+ }
+
+ // Construct an EPRT command
+ unsigned Proto = 0;
+ for (unsigned J = 0; AFMap[J].Family != 0; J++)
+ if (AFMap[J].Family == ((struct sockaddr *)&Addr)->sa_family)
+ Proto = AFMap[J].IETFFamily;
+ if (Proto == 0)
+ return _error->Error(_("Unknown address family %u (AF_*)"),
+ ((struct sockaddr *)&Addr)->sa_family);
+
+ // Send the EPRT command
unsigned int Tag;
string Msg;
- if (WriteMsg(Tag,Msg,"PORT %d,%d,%d,%d,%d,%d",
- (int) (badr >> 24) & 0xff, (int) (badr >> 16) & 0xff,
- (int) (badr >> 8) & 0xff, (int) badr & 0xff,
- (int) (bp >> 8) & 0xff, (int) bp & 0xff) == false)
+ if (WriteMsg(Tag,Msg,"EPRT |%u|%s|%s|",Proto,Name,Service) == false)
return false;
if (Tag >= 400)
- return _error->Error("Unable to send port command");
-
+ return _error->Error(_("EPRT failed, server said: %s"),Msg.c_str());
return true;
}
/*}}}*/
bool FTPConn::Finalize()
{
// Passive mode? Do nothing
- if (PasvAddr.sin_port != 0)
+ if (PasvAddr != 0)
return true;
// Close any old socket..
// Wait for someone to connect..
if (WaitFd(DataListenFd,false,TimeOut) == false)
- return _error->Error("Data socket connect timed out");
+ return _error->Error(_("Data socket connect timed out"));
// Accept the connection
struct sockaddr_in Addr;
socklen_t Len = sizeof(Addr);
DataFd = accept(DataListenFd,(struct sockaddr *)&Addr,&Len);
if (DataFd < 0)
- return _error->Errno("accept","Unable to accept connection");
+ return _error->Errno("accept",_("Unable to accept connection"));
close(DataListenFd);
DataListenFd = -1;
// ---------------------------------------------------------------------
/* This opens a data connection, sends REST and RETR and then
transfers the file over. */
-bool FTPConn::Get(const char *Path,FileFd &To,unsigned long Resume,
- MD5Summation &MD5,bool &Missing)
+bool FTPConn::Get(const char *Path,FileFd &To,unsigned long long Resume,
+ Hashes &Hash,bool &Missing, unsigned long long MaximumSize,
+ pkgAcqMethod *Owner)
{
Missing = false;
if (CreateDataFd() == false)
if (Resume != 0)
{
- if (MD5.AddFD(To.Fd(),Resume) == false)
+ if (Hash.AddFD(To,Resume) == false)
{
- _error->Errno("read","Problem hashing file");
+ _error->Errno("read",_("Problem hashing file"));
return false;
}
}
{
if (Tag == 550)
Missing = true;
- return _error->Error("Unable to fetch file, server said '%s'",Msg.c_str());
+ return _error->Error(_("Unable to fetch file, server said '%s'"),Msg.c_str());
}
// Finish off the data connection
if (WaitFd(DataFd,false,TimeOut) == false)
{
Close();
- return _error->Error("Data socket timed out");
+ return _error->Error(_("Data socket timed out"));
}
// Read the data..
break;
}
- MD5.Add(Buffer,Res);
+ Hash.Add(Buffer,Res);
if (To.Write(Buffer,Res) == false)
{
Close();
return false;
- }
+ }
+
+ if (MaximumSize > 0 && To.Tell() > MaximumSize)
+ {
+ Owner->SetFailReason("MaximumSizeExceeded");
+ return _error->Error("Writing more data than expected (%llu > %llu)",
+ To.Tell(), MaximumSize);
+ }
}
// All done
if (ReadResp(Tag,Msg) == false)
return false;
if (Tag >= 400)
- return _error->Error("Data transfer failed, server said '%s'",Msg.c_str());
+ return _error->Error(_("Data transfer failed, server said '%s'"),Msg.c_str());
return true;
}
/*}}}*/
// FtpMethod::FtpMethod - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
-FtpMethod::FtpMethod() : pkgAcqMethod("1.0",SendConfig)
+FtpMethod::FtpMethod() : aptMethod("ftp","1.0",SendConfig)
{
signal(SIGTERM,SigTerm);
signal(SIGINT,SigTerm);
/*}}}*/
// FtpMethod::SigTerm - Handle a fatal signal /*{{{*/
// ---------------------------------------------------------------------
-/* This closes and timestamps the open file. This is neccessary to get
+/* This closes and timestamps the open file. This is necessary to get
resume behavoir on user abort */
void FtpMethod::SigTerm(int)
{
if (FailFd == -1)
- exit(100);
- close(FailFd);
-
+ _exit(100);
+
// Timestamp
- struct utimbuf UBuf;
- UBuf.actime = FailTime;
- UBuf.modtime = FailTime;
- utime(FailFile.c_str(),&UBuf);
-
- exit(100);
+ struct timeval times[2];
+ times[0].tv_sec = FailTime;
+ times[1].tv_sec = FailTime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(FailFile.c_str(), times);
+
+ close(FailFd);
+
+ _exit(100);
}
/*}}}*/
// FtpMethod::Configuration - Handle a configuration message /*{{{*/
/* We stash the desired pipeline depth */
bool FtpMethod::Configuration(string Message)
{
- if (pkgAcqMethod::Configuration(Message) == false)
+ if (aptMethod::Configuration(Message) == false)
return false;
-
+
TimeOut = _config->FindI("Acquire::Ftp::Timeout",TimeOut);
+
return true;
}
/*}}}*/
FetchResult Res;
Res.Filename = Itm->DestFile;
Res.IMSHit = false;
-
+
+ maybe_add_auth (Get, _config->FindFile("Dir::Etc::netrc"));
+
// Connect to the server
if (Server == 0 || Server->Comp(Get) == false)
{
}
// Get the files information
- Status("Query");
- unsigned long Size;
+ Status(_("Query"));
+ unsigned long long Size;
if (Server->Size(File,Size) == false ||
Server->ModTime(File,FailTime) == false)
{
struct stat Buf;
if (stat(Itm->DestFile.c_str(),&Buf) == 0)
{
- if (Size == (unsigned)Buf.st_size && FailTime == Buf.st_mtime)
+ if (Size == (unsigned long long)Buf.st_size && FailTime == Buf.st_mtime)
{
Res.Size = Buf.st_size;
Res.LastModified = Buf.st_mtime;
+ Res.ResumePoint = Buf.st_size;
URIDone(Res);
return true;
}
// Resume?
- if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size)
+ if (FailTime == Buf.st_mtime && Size > (unsigned long long)Buf.st_size)
Res.ResumePoint = Buf.st_size;
}
// Open the file
- MD5Summation MD5;
+ Hashes Hash(Itm->ExpectedHashes);
{
FileFd Fd(Itm->DestFile,FileFd::WriteAny);
if (_error->PendingError() == true)
URIStart(Res);
FailFile = Itm->DestFile;
- FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
+ FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
FailFd = Fd.Fd();
bool Missing;
- if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing) == false)
+ if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing,Itm->MaximumSize,this) == false)
{
- // If the file is missing we hard fail otherwise transient fail
- if (Missing == true)
+ Fd.Close();
+
+ // Timestamp
+ struct timeval times[2];
+ times[0].tv_sec = FailTime;
+ times[1].tv_sec = FailTime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(FailFile.c_str(), times);
+
+ // If the file is missing we hard fail and delete the destfile
+ // otherwise transient fail
+ if (Missing == true) {
+ RemoveFile("ftp", FailFile);
return false;
+ }
Fail(true);
return true;
}
Res.Size = Fd.Size();
+
+ // Timestamp
+ struct timeval times[2];
+ times[0].tv_sec = FailTime;
+ times[1].tv_sec = FailTime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(Fd.Name().c_str(), times);
+ FailFd = -1;
}
-
+
Res.LastModified = FailTime;
- Res.MD5Sum = MD5.Result();
-
- // Timestamp
- struct utimbuf UBuf;
- time(&UBuf.actime);
- UBuf.actime = FailTime;
- UBuf.modtime = FailTime;
- utime(Queue->DestFile.c_str(),&UBuf);
- FailFd = -1;
+ Res.TakeHashes(Hash);
URIDone(Res);
-
+
return true;
}
/*}}}*/
-int main(int argc,const char *argv[])
+int main(int, const char *argv[])
{
+ setlocale(LC_ALL, "");
+
/* See if we should be come the http client - we do this for http
proxy urls */
if (getenv("ftp_proxy") != 0)
{
URI Proxy = string(getenv("ftp_proxy"));
+
+ // Run the HTTP method
if (Proxy.Access == "http")
{
// Copy over the environment setting
char S[300];
snprintf(S,sizeof(S),"http_proxy=%s",getenv("ftp_proxy"));
putenv(S);
+ putenv((char *)"no_proxy=");
// Run the http method
- string Path = flNotFile(argv[0]) + "/http";
- execl(Path.c_str(),Path.c_str(),0);
- cerr << "Unable to invoke " << Path << endl;
+ string Path = flNotFile(argv[0]) + "http";
+ execl(Path.c_str(),Path.c_str(),(char *)NULL);
+ cerr << _("Unable to invoke ") << Path << endl;
exit(100);
}
}
FtpMethod Mth;
-
+
return Mth.Run();
}