// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: http.cc,v 1.24 1999/01/30 08:23:49 jgg Exp $
+// $Id: http.cc,v 1.33 1999/05/28 07:04:45 jgg Exp $
/* ######################################################################
HTTP Aquire Method - This is the HTTP aquire method for APT.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
+#include <errno.h>
// Internet stuff
-#include <netinet/in.h>
+/*#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
-#include <netdb.h>
+#include <netdb.h>*/
+#include "rfc2553emu.h"
#include "http.h"
+
/*}}}*/
string HttpMethod::FailFile;
// ---------------------------------------------------------------------
/* This opens a connection to the server. */
string LastHost;
-in_addr LastHostA;
+int LastPort = 0;
+struct addrinfo *LastHostAddr = 0;
bool ServerState::Open()
{
// Use the already open connection if possible.
Proxy = getenv("http_proxy");
// Determine what host and port to use based on the proxy settings
- int Port = 80;
+ int Port = 0;
string Host;
if (Proxy.empty() == true)
{
/* 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)
+ if (LastHost != Host || LastPort != Port)
{
Owner->Status("Connecting to %s",Host.c_str());
// Lookup the host
- hostent *Addr = gethostbyname(Host.c_str());
- if (Addr == 0 || Addr->h_addr_list[0] == 0)
+ char S[30] = "http";
+ 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;
- LastHostA = *(in_addr *)(Addr->h_addr_list[0]);
+ LastPort = Port;
}
-
- Owner->Status("Connecting to %s (%s)",Host.c_str(),inet_ntoa(LastHostA));
+
+ // 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(AF_INET,SOCK_STREAM,0)) < 0)
+ if ((ServerFd = socket(LastHostAddr->ai_family,LastHostAddr->ai_socktype,
+ LastHostAddr->ai_protocol)) < 0)
return _error->Errno("socket","Could not create a socket");
-
- // Connect to the server
- struct sockaddr_in server;
- server.sin_family = AF_INET;
- server.sin_port = htons(Port);
- server.sin_addr = LastHostA;
SetNonBlock(ServerFd,true);
- if (connect(ServerFd,(sockaddr *)&server,sizeof(server)) < 0 &&
+ if (connect(ServerFd,LastHostAddr->ai_addr,LastHostAddr->ai_addrlen) < 0 &&
errno != EINPROGRESS)
- return _error->Errno("socket","Could not create a socket");
+ return _error->Errno("connect","Cannot initiate the connection "
+ "to %s (%s).",Host.c_str(),Name);
/* This implements a timeout for connect by opening the connection
nonblocking */
- fd_set wfds;
- FD_ZERO(&wfds);
- FD_SET(ServerFd,&wfds);
- struct timeval tv;
- tv.tv_sec = TimeOut;
- tv.tv_usec = 0;
- int Res = 0;
- if ((Res = select(ServerFd+1,0,&wfds,0,&tv)) < 0)
- return _error->Errno("select","Select failed");
- if (Res == 0)
- return _error->Error("Could not connect, connection timed out");
- unsigned int Err,Len=sizeof(Err);
+ 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.");
+ return _error->Error("Could not connect to %s (%s).",Host.c_str(),Name);
return true;
}
while ((Last = Owner->Go(false,this)) == true);
if (Last == false)
return false;
- return true;
+ return !_error->PendingError();
}
// Transfer the block
continue;
In.Limit(-1);
- return true;
+ return !_error->PendingError();
}
while (Owner->Go(true,this) == true);
}
- return Owner->Flush(this);
+ return Owner->Flush(this) && !_error->PendingError();
}
/*}}}*/
// ServerState::HeaderLine - Process a header line /*{{{*/
string::size_type Pos = Line.find(' ');
if (Pos == string::npos || Pos+1 > Line.length())
- return _error->Error("Bad header line");
-
- string Tag = string(Line,0,Pos);
- string Val = string(Line,Pos+1);
+ {
+ // Blah, some servers use "connection:closes", evil.
+ Pos = Line.find(':');
+ if (Pos == string::npos || Pos + 2 > Line.length())
+ return _error->Error("Bad header line");
+ Pos++;
+ }
+ // Parse off any trailing spaces between the : and the next word.
+ string::size_type Pos2 = Pos;
+ while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0)
+ Pos2++;
+
+ string Tag = string(Line,0,Pos);
+ string Val = string(Line,Pos2);
+
if (stringcasecmp(Tag.begin(),Tag.begin()+4,"HTTP") == 0)
{
// Evil servers return no version
/* */
bool HttpMethod::ServerDie(ServerState *Srv)
{
+ unsigned int LErrno = errno;
+
// Dump the buffer to the file
if (Srv->State == ServerState::Data)
{
Srv->Encoding != ServerState::Closes)
{
Srv->Close();
- if (errno == 0)
+ if (LErrno == 0)
return _error->Error("Error reading from server Remote end closed connection");
+ errno = LErrno;
return _error->Errno("read","Error reading from server");
}
else
return 5;
FailFile = Queue->DestFile;
- FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
+ FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
FailFd = File->Fd();
FailTime = Srv->Date;
// Timestamp
struct utimbuf UBuf;
- time(&UBuf.actime);
UBuf.actime = FailTime;
UBuf.modtime = FailTime;
utime(FailFile.c_str(),&UBuf);
int FailCounter = 0;
while (1)
- {
- if (FailCounter >= 2)
- {
- Fail("Massive Server Brain Damage",true);
- FailCounter = 0;
- }
-
+ {
// We have no commands, wait for some to arrive
if (Queue == 0)
{
FailCounter++;
_error->Discard();
Server->Close();
+
+ if (FailCounter >= 2)
+ {
+ Fail("Connection timed out",true);
+ FailCounter = 0;
+ }
+
continue;
}
};
URIDone(Res);
}
else
- Fail();
+ Fail(true);
break;
}