summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
authorArch Librarian <arch@canonical.com>2004-09-20 16:56:32 +0000
committerArch Librarian <arch@canonical.com>2004-09-20 16:56:32 +0000
commitb2e465d6d32d2dc884f58b94acb7e35f671a87fe (patch)
tree5928383b9bde7b0ba9812e6526ad746466e558f7 /methods
parent00b47c98ca4a4349686a082eba6d77decbb03a4d (diff)
Join with aliencode
Author: jgg Date: 2001-02-20 07:03:16 GMT Join with aliencode
Diffstat (limited to 'methods')
-rw-r--r--methods/connect.cc57
-rw-r--r--methods/connect.h3
-rw-r--r--methods/ftp.cc306
-rw-r--r--methods/ftp.h18
-rw-r--r--methods/http.cc24
-rw-r--r--methods/http.h5
-rw-r--r--methods/makefile16
-rw-r--r--methods/rfc2553emu.cc51
-rw-r--r--methods/rsh.cc486
-rw-r--r--methods/rsh.h69
10 files changed, 936 insertions, 99 deletions
diff --git a/methods/connect.cc b/methods/connect.cc
index d0dcde698..f3470af46 100644
--- a/methods/connect.cc
+++ b/methods/connect.cc
@@ -1,6 +1,6 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: connect.cc,v 1.6 2000/05/28 04:34:44 jgg Exp $
+// $Id: connect.cc,v 1.7 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
Connect - Replacement connect call
@@ -30,6 +30,18 @@ static int LastPort = 0;
static struct addrinfo *LastHostAddr = 0;
static struct addrinfo *LastUsed = 0;
+// RotateDNS - Select a new server from a DNS rotation /*{{{*/
+// ---------------------------------------------------------------------
+/* This is called during certain errors in order to recover by selecting a
+ new server */
+void RotateDNS()
+{
+ if (LastUsed != 0 && LastUsed->ai_next != 0)
+ LastUsed = LastUsed->ai_next;
+ else
+ LastUsed = LastHostAddr;
+}
+ /*}}}*/
// DoConnect - Attempt a connect operation /*{{{*/
// ---------------------------------------------------------------------
/* This helper function attempts a connection to a single address. */
@@ -39,17 +51,30 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
// Show a status indicator
char Name[NI_MAXHOST];
char Service[NI_MAXSERV];
- Name[0] = 0;
+
+ Name[0] = 0;
Service[0] = 0;
getnameinfo(Addr->ai_addr,Addr->ai_addrlen,
Name,sizeof(Name),Service,sizeof(Service),
NI_NUMERICHOST|NI_NUMERICSERV);
Owner->Status("Connecting to %s (%s)",Host.c_str(),Name);
-
+
+ /* If this is an IP rotation store the IP we are using.. If something goes
+ wrong this will get tacked onto the end of the error message */
+ if (LastHostAddr->ai_next != 0)
+ {
+ char Name2[NI_MAXHOST + NI_MAXSERV + 10];
+ snprintf(Name2,sizeof(Name2),"[IP: %s %s]",Name,Service);
+ Owner->SetFailExtraMsg(string(Name2));
+ }
+ else
+ Owner->SetFailExtraMsg("");
+
// Get a socket
if ((Fd = socket(Addr->ai_family,Addr->ai_socktype,
Addr->ai_protocol)) < 0)
- return _error->Errno("socket","Could not create a socket");
+ return _error->Errno("socket","Could not create a socket for %s (f=%u t=%u p=%u)",
+ Name,Addr->ai_family,Addr->ai_socktype,Addr->ai_protocol);
SetNonBlock(Fd,true);
if (connect(Fd,Addr->ai_addr,Addr->ai_addrlen) < 0 &&
@@ -62,7 +87,7 @@ static bool DoConnect(struct addrinfo *Addr,string Host,
if (WaitFd(Fd,true,TimeOut) == false)
return _error->Error("Could not connect to %s:%s (%s), "
"connection timed out",Host.c_str(),Service,Name);
-
+
// Check the socket for an error condition
unsigned int Err;
unsigned int Len = sizeof(Err);
@@ -134,8 +159,8 @@ bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
return _error->Error("Could not resolve '%s'",Host.c_str());
}
- return _error->Error("Something wicked happend resolving '%s:%s'",
- Host.c_str(),ServStr);
+ return _error->Error("Something wicked happened resolving '%s:%s' (%i)",
+ Host.c_str(),ServStr,Res);
}
break;
}
@@ -165,14 +190,22 @@ bool Connect(string Host,int Port,const char *Service,int DefPort,int &Fd,
CurHost = CurHost->ai_next;
}
while (CurHost != 0 && CurHost->ai_family == AF_UNIX);
-
- LastUsed = 0;
+
+ /* If we reached the end of the search list then wrap around to the
+ start */
+ if (CurHost == 0 && LastUsed != 0)
+ CurHost = LastHostAddr;
+
+ // Reached the end of the search cycle
+ if (CurHost == LastUsed)
+ break;
+
if (CurHost != 0)
_error->Discard();
- }
+ }
if (_error->PendingError() == true)
- return false;
- return _error->Error("Unable to connect to %s:",Host.c_str(),ServStr);
+ return false;
+ return _error->Error("Unable to connect to %s %s:",Host.c_str(),ServStr);
}
/*}}}*/
diff --git a/methods/connect.h b/methods/connect.h
index 1786a2480..6f208e31d 100644
--- a/methods/connect.h
+++ b/methods/connect.h
@@ -1,6 +1,6 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: connect.h,v 1.2 1999/07/18 23:06:56 jgg Exp $
+// $Id: connect.h,v 1.3 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
Connect - Replacement connect call
@@ -15,5 +15,6 @@
bool Connect(string To,int Port,const char *Service,int DefPort,
int &Fd,unsigned long TimeOut,pkgAcqMethod *Owner);
+void RotateDNS();
#endif
diff --git a/methods/ftp.cc b/methods/ftp.cc
index 57095c4bf..0d617dd8f 100644
--- a/methods/ftp.cc
+++ b/methods/ftp.cc
@@ -1,6 +1,6 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: ftp.cc,v 1.20 2000/06/18 04:19:39 jgg Exp $
+// $Id: ftp.cc,v 1.21 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
HTTP Aquire Method - This is the FTP aquire method for APT.
@@ -40,6 +40,20 @@
#include "ftp.h"
/*}}}*/
+/* 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},{}};
+#else
+struct AFMap AFMap[] = {{AF_INET,1},{AF_INET6,2},{}};
+#endif
+
unsigned long TimeOut = 120;
URI Proxy;
string FtpMethod::FailFile;
@@ -53,7 +67,7 @@ FTPConn::FTPConn(URI Srv) : Len(0), ServerFd(-1), DataFd(-1),
DataListenFd(-1), ServerName(Srv)
{
Debug = _config->FindB("Debug::Acquire::Ftp",false);
- memset(&PasvAddr,0,sizeof(PasvAddr));
+ PasvAddr = 0;
}
/*}}}*/
// FTPConn::~FTPConn - Destructor /*{{{*/
@@ -75,7 +89,10 @@ void FTPConn::Close()
DataFd = -1;
close(DataListenFd);
DataListenFd = -1;
- memset(&PasvAddr,0,sizeof(PasvAddr));
+
+ if (PasvAddr != 0)
+ freeaddrinfo(PasvAddr);
+ PasvAddr = 0;
}
/*}}}*/
// FTPConn::Open - Open a new connection /*{{{*/
@@ -89,7 +106,7 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
return true;
Close();
-
+
// Determine the proxy setting
if (getenv("ftp_proxy") == 0)
{
@@ -124,13 +141,22 @@ bool FTPConn::Open(pkgAcqMethod *Owner)
Host = Proxy.Host;
}
- // Connect to the remote server
+ /* 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;
- socklen_t Len = sizeof(Peer);
- if (getpeername(ServerFd,(sockaddr *)&Peer,&Len) != 0)
+
+ // 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");
+
Owner->Status("Logging in");
return Login();
}
@@ -179,7 +205,7 @@ bool FTPConn::Login()
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
{
@@ -236,6 +262,12 @@ bool FTPConn::Login()
}
}
+ // 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;
@@ -283,6 +315,8 @@ bool FTPConn::ReadLine(string &Text)
// 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");
@@ -409,10 +443,19 @@ bool FTPConn::WriteMsg(unsigned int &Ret,string &Text,const char *Fmt,...)
// ---------------------------------------------------------------------
/* 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;
@@ -422,41 +465,139 @@ bool FTPConn::GoPasv()
// 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++)
+ {
+ 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)
{
- PasvAddr.sin_addr = Peer.sin_addr;
+ // 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.
+ int Res;
+ if ((Res = getaddrinfo(IP.c_str(),PStr,&Hints,&PasvAddr)) != 0)
+ return true;
return true;
}
@@ -517,20 +658,21 @@ bool FTPConn::CreateDataFd()
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)
+ 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");
@@ -543,8 +685,8 @@ bool FTPConn::CreateDataFd()
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;
}
@@ -552,43 +694,91 @@ bool FTPConn::CreateDataFd()
close(DataListenFd);
DataListenFd = -1;
- // Get a socket
- if ((DataListenFd = socket(AF_INET,SOCK_STREAM,0)) < 0)
+ // Get the information for a listening socket.
+ struct addrinfo *BindAddr = 0;
+ 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;
+ int Res;
+ if ((Res = getaddrinfo(0,"0",&Hints,&BindAddr)) != 0)
+ 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)
+ 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");
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)
+ 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");
- 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
+ // 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("Unkonwn 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;
}
/*}}}*/
@@ -599,7 +789,7 @@ bool FTPConn::CreateDataFd()
bool FTPConn::Finalize()
{
// Passive mode? Do nothing
- if (PasvAddr.sin_port != 0)
+ if (PasvAddr != 0)
return true;
// Close any old socket..
diff --git a/methods/ftp.h b/methods/ftp.h
index 7416589a0..f791195b3 100644
--- a/methods/ftp.h
+++ b/methods/ftp.h
@@ -1,5 +1,6 @@
// -*- mode: cpp; mode: fold -*-
-// Description /*{{{*/// $Id: ftp.h,v 1.2 1999/03/15 07:20:41 jgg Exp $
+// Description /*{{{*/// $Id: ftp.h,v 1.3 2001/02/20 07:03:18 jgg Exp $
+// $Id: ftp.h,v 1.3 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
FTP Aquire Method - This is the FTP aquire method for APT.
@@ -17,12 +18,20 @@ class FTPConn
int DataFd;
int DataListenFd;
URI ServerName;
+ bool ForceExtended;
bool TryPassive;
bool Debug;
-
- struct sockaddr_in PasvAddr;
- struct sockaddr_in Peer;
+ struct addrinfo *PasvAddr;
+
+ // Generic Peer Address
+ struct sockaddr_storage PeerAddr;
+ socklen_t PeerAddrLen;
+
+ // Generic Server Address (us)
+ struct sockaddr_storage ServerAddr;
+ socklen_t ServerAddrLen;
+
// Private helper functions
bool ReadLine(string &Text);
bool Login();
@@ -41,6 +50,7 @@ class FTPConn
bool Open(pkgAcqMethod *Owner);
void Close();
bool GoPasv();
+ bool ExtGoPasv();
// Query
bool Size(const char *Path,unsigned long &Size);
diff --git a/methods/http.cc b/methods/http.cc
index f52459377..7347e8349 100644
--- a/methods/http.cc
+++ b/methods/http.cc
@@ -1,6 +1,6 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: http.cc,v 1.46 2000/05/28 04:33:59 jgg Exp $
+// $Id: http.cc,v 1.47 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
HTTP Aquire Method - This is the HTTP aquire method for APT.
@@ -285,7 +285,7 @@ bool ServerState::Open()
else
Proxy = getenv("http_proxy");
- // Parse no_proxy, a , seperated list of hosts
+ // Parse no_proxy, a , separated list of hosts
if (getenv("no_proxy") != 0)
{
const char *Start = getenv("no_proxy");
@@ -376,6 +376,10 @@ int ServerState::RunHeaders()
I = J;
}
+ // 100 Continue is a Nop...
+ if (Result == 100)
+ continue;
+
// Tidy up the connection persistance state.
if (Encoding == Closes && HaveContent == true)
Persistent = false;
@@ -537,7 +541,7 @@ bool ServerState::HeaderLine(string Line)
else
Persistent = true;
}
-
+
return true;
}
@@ -676,6 +680,10 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
Req += string("Proxy-Authorization: Basic ") +
Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
+ if (Uri.User.empty() == false || Uri.Password.empty() == false)
+ Req += string("Authorization: Basic ") +
+ Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
+
Req += "User-Agent: Debian APT-HTTP/1.2\r\n\r\n";
if (Debug == true)
@@ -1058,6 +1066,7 @@ int HttpMethod::Loop()
{
_error->Error("Bad header Data");
Fail(true);
+ RotateDNS();
continue;
}
@@ -1076,6 +1085,7 @@ int HttpMethod::Loop()
FailCounter = 0;
}
+ RotateDNS();
continue;
}
};
@@ -1093,6 +1103,11 @@ int HttpMethod::Loop()
// Run the data
bool Result = Server->RunData();
+ /* If the server is sending back sizeless responses then fill in
+ the size now */
+ if (Res.Size == 0)
+ Res.Size = File->Size();
+
// Close the file, destroy the FD object and timestamp it
FailFd = -1;
delete File;
@@ -1108,7 +1123,7 @@ int HttpMethod::Loop()
// Send status to APT
if (Result == true)
{
- Res.MD5Sum = Server->In.MD5->Result();
+ Res.MD5Sum = Server->In.MD5->Result();
URIDone(Res);
}
else
@@ -1135,6 +1150,7 @@ int HttpMethod::Loop()
case 5:
{
Fail();
+ RotateDNS();
Server->Close();
break;
}
diff --git a/methods/http.h b/methods/http.h
index 0c916707b..2569c2921 100644
--- a/methods/http.h
+++ b/methods/http.h
@@ -1,5 +1,6 @@
// -*- mode: cpp; mode: fold -*-
-// Description /*{{{*/// $Id: http.h,v 1.8 2000/05/28 04:33:59 jgg Exp $
+// Description /*{{{*/// $Id: http.h,v 1.9 2001/02/20 07:03:18 jgg Exp $
+// $Id: http.h,v 1.9 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
HTTP Aquire Method - This is the HTTP aquire method for APT.
@@ -133,7 +134,7 @@ class HttpMethod : public pkgAcqMethod
static void SigTerm(int);
public:
- friend ServerState;
+ friend class ServerState;
FileFd *File;
ServerState *Server;
diff --git a/methods/makefile b/methods/makefile
index e8eaec230..dfab23ab2 100644
--- a/methods/makefile
+++ b/methods/makefile
@@ -47,3 +47,19 @@ SLIBS = -lapt-pkg $(SOCKETLIBS)
LIB_MAKES = apt-pkg/makefile
SOURCE = ftp.cc rfc2553emu.cc connect.cc
include $(PROGRAM_H)
+
+# The rsh method
+PROGRAM=rsh
+SLIBS = -lapt-pkg
+LIB_MAKES = apt-pkg/makefile
+SOURCE = rsh.cc
+include $(PROGRAM_H)
+
+# SSH method symlink
+all: $(BIN)/ssh
+veryclean: clean-$(BIN)/ssh
+$(BIN)/ssh:
+ echo "Installing ssh method link"
+ ln -fs rsh $(BIN)/ssh
+clean-$(BIN)/ssh:
+ rm $(BIN)/ssh
diff --git a/methods/rfc2553emu.cc b/methods/rfc2553emu.cc
index 22daa2231..66bc906e9 100644
--- a/methods/rfc2553emu.cc
+++ b/methods/rfc2553emu.cc
@@ -1,6 +1,6 @@
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: rfc2553emu.cc,v 1.7 2000/06/18 06:04:45 jgg Exp $
+// $Id: rfc2553emu.cc,v 1.8 2001/02/20 07:03:18 jgg Exp $
/* ######################################################################
RFC 2553 Emulation - Provides emulation for RFC 2553 getaddrinfo,
@@ -36,20 +36,6 @@ int getaddrinfo(const char *nodename, const char *servname,
const char *End;
char **CurAddr;
- Addr = gethostbyname(nodename);
- if (Addr == 0)
- {
- if (h_errno == TRY_AGAIN)
- return EAI_AGAIN;
- if (h_errno == NO_RECOVERY)
- return EAI_FAIL;
- return EAI_NONAME;
- }
-
- // No A records
- if (Addr->h_addr_list[0] == 0)
- return EAI_NONAME;
-
// Try to convert the service as a number
Port = htons(strtol(servname,(char **)&End,0));
Proto = SOCK_STREAM;
@@ -86,10 +72,32 @@ int getaddrinfo(const char *nodename, const char *servname,
hints->ai_socktype != 0)
return EAI_SERVICE;
}
+
+ // Hostname lookup, only if this is not a listening socket
+ if (hints != 0 && (hints->ai_flags & AI_PASSIVE) != AI_PASSIVE)
+ {
+ Addr = gethostbyname(nodename);
+ if (Addr == 0)
+ {
+ if (h_errno == TRY_AGAIN)
+ return EAI_AGAIN;
+ if (h_errno == NO_RECOVERY)
+ return EAI_FAIL;
+ return EAI_NONAME;
+ }
+
+ // No A records
+ if (Addr->h_addr_list[0] == 0)
+ return EAI_NONAME;
+
+ CurAddr = Addr->h_addr_list;
+ }
+ else
+ CurAddr = (char **)&End; // Fake!
// Start constructing the linked list
*res = 0;
- for (CurAddr = Addr->h_addr_list; *CurAddr != 0; CurAddr++)
+ for (; *CurAddr != 0; CurAddr++)
{
// New result structure
*Result = (struct addrinfo *)calloc(sizeof(**Result),1);
@@ -124,8 +132,15 @@ int getaddrinfo(const char *nodename, const char *servname,
// Set the address
((struct sockaddr_in *)(*Result)->ai_addr)->sin_family = AF_INET;
((struct sockaddr_in *)(*Result)->ai_addr)->sin_port = Port;
- ((struct sockaddr_in *)(*Result)->ai_addr)->sin_addr = *(in_addr *)(*CurAddr);
-
+
+ if (hints != 0 && (hints->ai_flags & AI_PASSIVE) != AI_PASSIVE)
+ ((struct sockaddr_in *)(*Result)->ai_addr)->sin_addr = *(in_addr *)(*CurAddr);
+ else
+ {
+ // Already zerod by calloc.
+ break;
+ }
+
Result = &(*Result)->ai_next;
}
diff --git a/methods/rsh.cc b/methods/rsh.cc
new file mode 100644
index 000000000..9e521edec
--- /dev/null
+++ b/methods/rsh.cc
@@ -0,0 +1,486 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+// $Id: rsh.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $
+/* ######################################################################
+
+ RSH method - Transfer files via rsh compatible program
+
+ Written by Ben Collins <bcollins@debian.org>, Copyright (c) 2000
+ Licensed under the GNU General Public License v2 [no exception clauses]
+
+ ##################################################################### */
+ /*}}}*/
+// Iclude Files /*{{{*/
+#include "rsh.h"
+#include <apt-pkg/error.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>
+ /*}}}*/
+
+const char *Prog;
+unsigned long TimeOut = 120;
+time_t RSHMethod::FailTime = 0;
+string RSHMethod::FailFile;
+int RSHMethod::FailFd = -1;
+
+// RSHConn::RSHConn - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+RSHConn::RSHConn(URI Srv) : Len(0), WriteFd(-1), ReadFd(-1),
+ ServerName(Srv), Process(-1) {}
+ /*}}}*/
+// RSHConn::RSHConn - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+RSHConn::~RSHConn()
+{
+ Close();
+}
+ /*}}}*/
+// RSHConn::Close - Forcibly terminate the connection /*{{{*/
+// ---------------------------------------------------------------------
+/* Often this is called when things have gone wrong to indicate that the
+ connection is no longer usable. */
+void RSHConn::Close()
+{
+ if (Process == -1)
+ return;
+
+ close(WriteFd);
+ close(ReadFd);
+ kill(Process,SIGINT);
+ ExecWait(Process,"",true);
+ WriteFd = -1;
+ ReadFd = -1;
+ Process = -1;
+}
+ /*}}}*/
+// RSHConn::Open - Connect to a host /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHConn::Open()
+{
+ // Use the already open connection if possible.
+ if (Process != -1)
+ return true;
+
+ if (Connect(ServerName.Host,ServerName.User) == false)
+ return false;
+
+ return true;
+}
+ /*}}}*/
+// RSHConn::Connect - Fire up rsh and connect /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHConn::Connect(string Host, string User)
+{
+ // Create the pipes
+ int Pipes[4] = {-1,-1,-1,-1};
+ if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
+ {
+ _error->Errno("pipe","Failed to create IPC pipe to subprocess");
+ for (int I = 0; I != 4; I++)
+ close(Pipes[I]);
+ return false;
+ }
+ for (int I = 0; I != 4; I++)
+ SetCloseExec(Pipes[I],true);
+
+ Process = ExecFork();
+
+ // The child
+ if (Process == 0)
+ {
+ const char *Args[6];
+ int i = 0;
+
+ dup2(Pipes[1],STDOUT_FILENO);
+ dup2(Pipes[2],STDIN_FILENO);
+
+ // Probably should do
+ // dup2(open("/dev/null",O_RDONLY),STDERR_FILENO);
+
+ Args[i++] = Prog;
+ if (User.empty() == false) {
+ Args[i++] = "-l";
+ Args[i++] = User.c_str();
+ }
+ if (Host.empty() == false) {
+ Args[i++] = Host.c_str();
+ }
+ Args[i++] = "/bin/sh";
+ Args[i] = 0;
+ execvp(Args[0],(char **)Args);
+ exit(100);
+ }
+
+ ReadFd = Pipes[0];
+ WriteFd = Pipes[3];
+ SetNonBlock(Pipes[0],true);
+ SetNonBlock(Pipes[3],true);
+ close(Pipes[1]);
+ close(Pipes[2]);
+
+ return true;
+}
+ /*}}}*/
+// RSHConn::ReadLine - Very simple buffered read with timeout /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHConn::ReadLine(string &Text)
+{
+ if (Process == -1 || ReadFd == -1)
+ return false;
+
+ // Suck in a line
+ while (Len < sizeof(Buffer))
+ {
+ // Scan the buffer for a new line
+ for (unsigned int I = 0; I != Len; I++)
+ {
+ // Escape some special chars
+ if (Buffer[I] == 0)
+ Buffer[I] = '?';
+
+ // End of line?
+ if (Buffer[I] != '\n')
+ continue;
+
+ I++;
+ Text = string(Buffer,I);
+ memmove(Buffer,Buffer+I,Len - I);
+ Len -= I;
+ return true;
+ }
+
+ // Wait for some data..
+ if (WaitFd(ReadFd,false,TimeOut) == false)
+ {
+ Close();
+ return _error->Error("Connection timeout");
+ }
+
+ // Suck it back
+ int Res = read(ReadFd,Buffer + Len,sizeof(Buffer) - Len);
+ if (Res <= 0)
+ {
+ _error->Errno("read","Read error");
+ Close();
+ return false;
+ }
+ Len += Res;
+ }
+
+ return _error->Error("A response overflowed the buffer.");
+}
+ /*}}}*/
+// RSHConn::WriteMsg - Send a message with optional remote sync. /*{{{*/
+// ---------------------------------------------------------------------
+/* The remote sync flag appends a || echo which will insert blank line
+ once the command completes. */
+bool RSHConn::WriteMsg(string &Text,bool Sync,const char *Fmt,...)
+{
+ va_list args;
+ va_start(args,Fmt);
+
+ // sprintf the description
+ char S[512];
+ vsnprintf(S,sizeof(S) - 4,Fmt,args);
+ if (Sync == true)
+ strcat(S," 2> /dev/null || echo\n");
+ else
+ strcat(S," 2> /dev/null\n");
+
+ // Send it off
+ unsigned long Len = strlen(S);
+ unsigned long Start = 0;
+ while (Len != 0)
+ {
+ if (WaitFd(WriteFd,true,TimeOut) == false)
+ {
+
+ Close();
+ return _error->Error("Connection timeout");
+ }
+
+ int Res = write(WriteFd,S + Start,Len);
+ if (Res <= 0)
+ {
+ _error->Errno("write","Write Error");
+ Close();
+ return false;
+ }
+
+ Len -= Res;
+ Start += Res;
+ }
+
+ if (Sync == true)
+ return ReadLine(Text);
+ return true;
+}
+ /*}}}*/
+// RSHConn::Size - Return the size of the file /*{{{*/
+// ---------------------------------------------------------------------
+/* Right now for successfull transfer the file size must be known in
+ advance. */
+bool RSHConn::Size(const char *Path,unsigned long &Size)
+{
+ // Query the size
+ string Msg;
+ Size = 0;
+
+ if (WriteMsg(Msg,true,"find %s -follow -printf '%%s\\n'",Path) == false)
+ return false;
+
+ // FIXME: Sense if the bad reply is due to a File Not Found.
+
+ char *End;
+ Size = strtoul(Msg.c_str(),&End,10);
+ if (End == Msg.c_str())
+ return _error->Error("File Not Found");
+ return true;
+}
+ /*}}}*/
+// RSHConn::ModTime - Get the modification time in UTC /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHConn::ModTime(const char *Path, time_t &Time)
+{
+ Time = time(&Time);
+ // Query the mod time
+ string Msg;
+
+ if (WriteMsg(Msg,true,"TZ=UTC find %s -follow -printf '%%TY%%Tm%%Td%%TH%%TM%%TS\\n'",Path) == false)
+ return false;
+
+ // Parse it
+ StrToTime(Msg,Time);
+ return true;
+}
+ /*}}}*/
+// RSHConn::Get - Get a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHConn::Get(const char *Path,FileFd &To,unsigned long Resume,
+ MD5Summation &MD5,bool &Missing, unsigned long Size)
+{
+ Missing = false;
+
+ // Round to a 2048 byte block
+ Resume = Resume - (Resume % 2048);
+
+ if (To.Truncate(Resume) == false)
+ return false;
+ if (To.Seek(0) == false)
+ return false;
+
+ if (Resume != 0) {
+ if (MD5.AddFD(To.Fd(),Resume) == false) {
+ _error->Errno("read","Problem hashing file");
+ return false;
+ }
+ }
+
+ // FIXME: Detect file-not openable type errors.
+ string Jnk;
+ if (WriteMsg(Jnk,false,"dd if=%s bs=2048 skip=%u", Path, Resume / 2048) == false)
+ return false;
+
+ // Copy loop
+ unsigned int MyLen = Resume;
+ unsigned char Buffer[4096];
+ while (MyLen < Size)
+ {
+ // Wait for some data..
+ if (WaitFd(ReadFd,false,TimeOut) == false)
+ {
+ Close();
+ return _error->Error("Data socket timed out");
+ }
+
+ // Read the data..
+ int Res = read(ReadFd,Buffer,sizeof(Buffer));
+ if (Res == 0)
+ {
+ Close();
+ return _error->Error("Connection closed prematurely");
+ }
+
+ if (Res < 0)
+ {
+ if (errno == EAGAIN)
+ continue;
+ break;
+ }
+ MyLen += Res;
+
+ MD5.Add(Buffer,Res);
+ if (To.Write(Buffer,Res) == false)
+ {
+ Close();
+ return false;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+
+// RSHMethod::RSHMethod - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+RSHMethod::RSHMethod() : pkgAcqMethod("1.0")
+{
+ signal(SIGTERM,SigTerm);
+ signal(SIGINT,SigTerm);
+ Server = 0;
+ FailFd = -1;
+};
+ /*}}}*/
+// RSHMethod::SigTerm - Clean up and timestamp the files on exit /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void RSHMethod::SigTerm(int sig)
+{
+ if (FailFd == -1)
+ _exit(100);
+ close(FailFd);
+
+ // Timestamp
+ struct utimbuf UBuf;
+ UBuf.actime = FailTime;
+ UBuf.modtime = FailTime;
+ utime(FailFile.c_str(),&UBuf);
+
+ _exit(100);
+}
+ /*}}}*/
+// RSHMethod::Fetch - Fetch a URI /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool RSHMethod::Fetch(FetchItem *Itm)
+{
+ URI Get = Itm->Uri;
+ const char *File = Get.Path.c_str();
+ FetchResult Res;
+ Res.Filename = Itm->DestFile;
+ Res.IMSHit = false;
+
+ // Connect to the server
+ if (Server == 0 || Server->Comp(Get) == false) {
+ delete Server;
+ Server = new RSHConn(Get);
+ }
+
+ // Could not connect is a transient error..
+ if (Server->Open() == false) {
+ Server->Close();
+ Fail(true);
+ return true;
+ }
+
+ // We say this mainly because the pause here is for the
+ // ssh connection that is still going
+ Status("Connecting to %s", Get.Host.c_str());
+
+ // Get the files information
+ unsigned long Size;
+ if (Server->Size(File,Size) == false ||
+ Server->ModTime(File,FailTime) == false)
+ {
+ //Fail(true);
+ //_error->Error("File Not Found"); // Will be handled by Size
+ return false;
+ }
+ Res.Size = Size;
+
+ // See if it is an IMS hit
+ if (Itm->LastModified == FailTime) {
+ Res.Size = 0;
+ Res.IMSHit = true;
+ URIDone(Res);
+ return true;
+ }
+
+ // See if the file exists
+ struct stat Buf;
+ if (stat(Itm->DestFile.c_str(),&Buf) == 0) {
+ if (Size == (unsigned)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)
+ Res.ResumePoint = Buf.st_size;
+ }
+
+ // Open the file
+ MD5Summation MD5;
+ {
+ FileFd Fd(Itm->DestFile,FileFd::WriteAny);
+ if (_error->PendingError() == true)
+ return false;
+
+ URIStart(Res);
+
+ FailFile = Itm->DestFile;
+ FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
+ FailFd = Fd.Fd();
+
+ bool Missing;
+ if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing,Res.Size) == false)
+ {
+ Fd.Close();
+
+ // Timestamp
+ struct utimbuf UBuf;
+ UBuf.actime = FailTime;
+ UBuf.modtime = FailTime;
+ utime(FailFile.c_str(),&UBuf);
+
+ // If the file is missing we hard fail otherwise transient fail
+ if (Missing == true)
+ return false;
+ Fail(true);
+ return true;
+ }
+
+ Res.Size = Fd.Size();
+ }
+
+ Res.LastModified = FailTime;
+ Res.MD5Sum = MD5.Result();
+
+ // Timestamp
+ struct utimbuf UBuf;
+ UBuf.actime = FailTime;
+ UBuf.modtime = FailTime;
+ utime(Queue->DestFile.c_str(),&UBuf);
+ FailFd = -1;
+
+ URIDone(Res);
+
+ return true;
+}
+ /*}}}*/
+
+int main(int argc, const char *argv[])
+{
+ RSHMethod Mth;
+ Prog = strrchr(argv[0],'/');
+ Prog++;
+ return Mth.Run();
+}
diff --git a/methods/rsh.h b/methods/rsh.h
new file mode 100644
index 000000000..c3f3258e9
--- /dev/null
+++ b/methods/rsh.h
@@ -0,0 +1,69 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/// $Id: rsh.h,v 1.2 2001/02/20 07:03:18 jgg Exp $
+// $Id: rsh.h,v 1.2 2001/02/20 07:03:18 jgg Exp $
+/* ######################################################################
+
+ RSH method - Transfer files via rsh compatible program
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef APT_RSH_H
+#define APT_RSH_H
+
+#include <string>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/md5.h>
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/fileutl.h>
+
+class RSHConn
+{
+ char Buffer[1024*10];
+ unsigned long Len;
+ int WriteFd;
+ int ReadFd;
+ URI ServerName;
+
+ // Private helper functions
+ bool ReadLine(string &Text);
+
+ public:
+
+ int Process;
+
+ // Raw connection IO
+ bool WriteMsg(string &Text,bool Sync,const char *Fmt,...);
+ bool Connect(string Host, string User);
+ bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
+
+ // Connection control
+ bool Open();
+ void Close();
+
+ // Query
+ bool Size(const char *Path,unsigned long &Size);
+ bool ModTime(const char *Path, time_t &Time);
+ bool Get(const char *Path,FileFd &To,unsigned long Resume,
+ MD5Summation &MD5,bool &Missing, unsigned long Size);
+
+ RSHConn(URI Srv);
+ ~RSHConn();
+};
+
+class RSHMethod : public pkgAcqMethod
+{
+ virtual bool Fetch(FetchItem *Itm);
+
+ RSHConn *Server;
+
+ static string FailFile;
+ static int FailFd;
+ static time_t FailTime;
+ static void SigTerm(int);
+
+ public:
+
+ RSHMethod();
+};
+
+#endif