diff options
author | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
---|---|---|
committer | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
commit | b2e465d6d32d2dc884f58b94acb7e35f671a87fe (patch) | |
tree | 5928383b9bde7b0ba9812e6526ad746466e558f7 /methods | |
parent | 00b47c98ca4a4349686a082eba6d77decbb03a4d (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.cc | 57 | ||||
-rw-r--r-- | methods/connect.h | 3 | ||||
-rw-r--r-- | methods/ftp.cc | 306 | ||||
-rw-r--r-- | methods/ftp.h | 18 | ||||
-rw-r--r-- | methods/http.cc | 24 | ||||
-rw-r--r-- | methods/http.h | 5 | ||||
-rw-r--r-- | methods/makefile | 16 | ||||
-rw-r--r-- | methods/rfc2553emu.cc | 51 | ||||
-rw-r--r-- | methods/rsh.cc | 486 | ||||
-rw-r--r-- | methods/rsh.h | 69 |
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 |