-extern "C" {
- #include <mach-o/nlist.h>
-}
-
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
// $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
/* ######################################################################
- HTTP Aquire Method - This is the HTTP aquire method for APT.
+ HTTP Acquire Method - This is the HTTP aquire method for APT.
It uses HTTP/1.1 and many of the fancy options there-in, such as
pipelining, range, if-range and so on.
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/error.h>
#include <apt-pkg/hashes.h>
+#include <apt-pkg/netrc.h>
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <iostream>
+#include <map>
+#include <set>
#include <apti18n.h>
+
// Internet stuff
#include <netdb.h>
#include <arpa/inet.h>
+#include <dlfcn.h>
+#include <lockdown.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SystemConfiguration.h>
+#include "config.h"
#include "connect.h"
#include "rfc2553emu.h"
#include "http.h"
-
/*}}}*/
using namespace std;
CFStringRef Firmware_;
const char *Machine_;
-const char *SerialNumber_;
+CFStringRef UniqueID_;
void CfrsError(const char *name, CFReadStreamRef rs) {
CFStreamError se = CFReadStreamGetError(rs);
time_t HttpMethod::FailTime = 0;
unsigned long PipelineDepth = 10;
unsigned long TimeOut = 120;
+bool AllowRedirect = false;
bool Debug = false;
+URI Proxy;
unsigned long CircleBuf::BwReadLimit=0;
unsigned long CircleBuf::BwTickReadData=0;
Persistent = true;
// Determine the proxy setting
- if (getenv("http_proxy") == 0)
+ string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
+ if (!SpecificProxy.empty())
{
- string DefProxy = _config->Find("Acquire::http::Proxy");
- string SpecificProxy = _config->Find("Acquire::http::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("http_proxy");
+ {
+ string DefProxy = _config->Find("Acquire::http::Proxy");
+ if (!DefProxy.empty())
+ {
+ Proxy = DefProxy;
+ }
+ else
+ {
+ char* result = getenv("http_proxy");
+ Proxy = result ? result : "";
+ }
+ }
// Parse no_proxy, a , separated list of domains
if (getenv("no_proxy") != 0)
/*}}}*/
// ServerState::RunHeaders - Get the headers before the data /*{{{*/
// ---------------------------------------------------------------------
-/* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
- parse error occured */
+/* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
+ parse error occurred */
int ServerState::RunHeaders()
{
State = Header;
// Evil servers return no version
if (Line[4] == '/')
{
- if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
+ if (sscanf(Line.c_str(),"HTTP/%u.%u %u%[^\n]",&Major,&Minor,
&Result,Code) != 4)
return _error->Error(_("The HTTP server sent an invalid reply header"));
}
{
Major = 0;
Minor = 9;
- if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
+ if (sscanf(Line.c_str(),"HTTP %u%[^\n]",&Result,Code) != 2)
return _error->Error(_("The HTTP server sent an invalid reply header"));
}
return true;
}
+ if (stringcasecmp(Tag,"Location:") == 0)
+ {
+ Location = Val;
+ return true;
+ }
+
return true;
}
/*}}}*/
+static const CFOptionFlags kNetworkEvents =
+ kCFStreamEventOpenCompleted |
+ kCFStreamEventHasBytesAvailable |
+ kCFStreamEventEndEncountered |
+ kCFStreamEventErrorOccurred |
+0;
+
+static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType event, void *arg) {
+ switch (event) {
+ case kCFStreamEventOpenCompleted:
+ break;
+
+ case kCFStreamEventHasBytesAvailable:
+ case kCFStreamEventEndEncountered:
+ *reinterpret_cast<int *>(arg) = 1;
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ break;
+
+ case kCFStreamEventErrorOccurred:
+ *reinterpret_cast<int *>(arg) = -1;
+ CFRunLoopStop(CFRunLoopGetCurrent());
+ break;
+ }
+}
+
+/* http://lists.apple.com/archives/Macnetworkprog/2006/Apr/msg00014.html */
+int CFReadStreamOpen(CFReadStreamRef stream, double timeout) {
+ CFStreamClientContext context;
+ int value(0);
+
+ memset(&context, 0, sizeof(context));
+ context.info = &value;
+
+ if (CFReadStreamSetClient(stream, kNetworkEvents, CFReadStreamCallback, &context)) {
+ CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
+ if (CFReadStreamOpen(stream))
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
+ else
+ value = -1;
+ CFReadStreamSetClient(stream, kCFStreamEventNone, NULL, NULL);
+ }
+
+ return value;
+}
+
// HttpMethod::SendReq - Send the HTTP request /*{{{*/
// ---------------------------------------------------------------------
/* This places the http request in the outbound buffer */
Req += string("Proxy-Authorization: Basic ") +
Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
+ maybe_add_auth (Uri, _config->FindFile("Dir::Etc::netrc"));
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.3\r\n\r\n";
+ }
+ Req += "User-Agent: " + _config->Find("Acquire::http::User-Agent",
+ "Debian APT-HTTP/1.3 ("VERSION")") + "\r\n\r\n";
if (Debug == true)
cerr << Req << endl;
1 - IMS hit
3 - Unrecoverable error
4 - Error with error content page
- 5 - Unrecoverable non-server error (close the connection) */
+ 5 - Unrecoverable non-server error (close the connection)
+ 6 - Try again with a new or changed URI
+ */
int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
{
// Not Modified
return 1;
}
+ /* Redirect
+ *
+ * Note that it is only OK for us to treat all redirection the same
+ * because we *always* use GET, not other HTTP methods. There are
+ * three redirection codes for which it is not appropriate that we
+ * redirect. Pass on those codes so the error handling kicks in.
+ */
+ if (AllowRedirect
+ && (Srv->Result > 300 && Srv->Result < 400)
+ && (Srv->Result != 300 // Multiple Choices
+ && Srv->Result != 304 // Not Modified
+ && Srv->Result != 306)) // (Not part of HTTP/1.1, reserved)
+ {
+ if (!Srv->Location.empty())
+ {
+ NextURI = Srv->Location;
+ return 6;
+ }
+ /* else pass through for error message */
+ }
+
/* We have a reply we dont handle. This should indicate a perm server
failure */
if (Srv->Result < 200 || Srv->Result >= 300)
if (Srv->StartPos >= 0)
{
Res.ResumePoint = Srv->StartPos;
- ftruncate(File->Fd(),Srv->StartPos);
+ if (ftruncate(File->Fd(),Srv->StartPos) < 0)
+ _error->Errno("ftruncate", _("Failed to truncate file"));
}
// Set the start point
// Queue the requests
int Depth = -1;
- bool Tail = false;
for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth;
I = I->Next, Depth++)
{
if (Server->Comp(I->Uri) == false)
break;
if (QueueBack == I)
- Tail = true;
- if (Tail == true)
{
QueueBack = I->Next;
SendReq(I,Server->Out);
if (pkgAcqMethod::Configuration(Message) == false)
return false;
+ AllowRedirect = _config->FindB("Acquire::http::AllowRedirect",true);
TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut);
PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth",
PipelineDepth);
/* */
int HttpMethod::Loop()
{
+ typedef vector<string> StringVector;
+ typedef vector<string>::iterator StringVectorIterator;
+ map<string, StringVector> Redirected;
+
signal(SIGTERM,SigTerm);
signal(SIGINT,SigTerm);
Server = 0;
+
+ std::set<std::string> cached;
int FailCounter = 0;
while (1)
URI uri = std::string(url);
std::string hs = uri.Host;
-#if __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ >= 10200
- struct hostent *he = gethostbyname(hs.c_str());
- if (he == NULL || he->h_addr_list[0] == NULL) {
- _error->Error(hstrerror(h_errno));
+ if (cached.find(hs) != cached.end()) {
+ _error->Error("Cached Failure");
Fail(true);
free(url);
+ FailCounter = 0;
+ continue;
}
- uri.Host = inet_ntoa(* (struct in_addr *) he->h_addr_list[0]);
-#endif
-
std::string urs = uri;
+ for (;;) {
+ size_t bad = urs.find_first_of("+");
+ if (bad == std::string::npos)
+ break;
+ // XXX: generalize
+ urs = urs.substr(0, bad) + "%2b" + urs.substr(bad + 1);
+ }
+
CFStringRef sr = CFStringCreateWithCString(kCFAllocatorDefault, urs.c_str(), se);
CFURLRef ur = CFURLCreateWithString(kCFAllocatorDefault, sr, NULL);
CFRelease(sr);
sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se);
CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Range"), sr);
CFRelease(sr);
+
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache"));
} else if (Queue->LastModified != 0) {
- sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se);
+ sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(Queue->LastModified).c_str(), se);
CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr);
CFRelease(sr);
- }
+
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache"));
+ } else
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("max-age=0"));
if (Firmware_ != NULL)
CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Firmware"), Firmware_);
CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr);
CFRelease(sr);
- sr = CFStringCreateWithCString(kCFAllocatorDefault, SerialNumber_, se);
- CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Serial-Number"), sr);
- CFRelease(sr);
+ if (UniqueID_ != NULL)
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Unique-ID"), UniqueID_);
- CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.98"));
-
-#if __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ >= 10200
- sr = CFStringCreateWithCString(kCFAllocatorDefault, hs.c_str(), se);
- CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Host"), sr);
- CFRelease(sr);
-#endif
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.592"));
CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm);
CFRelease(hm);
+#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
+#define _kCFStreamPropertyWriteTimeout CFSTR("_kCFStreamPropertyWriteTimeout")
+#define _kCFStreamPropertySocketImmediateBufferTimeOut CFSTR("_kCFStreamPropertySocketImmediateBufferTimeOut")
+
+ /*SInt32 to(TimeOut);
+ CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &to));*/
+ double to(TimeOut);
+ CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &to));
+
+ CFReadStreamSetProperty(rs, _kCFStreamPropertyReadTimeout, nm);
+ CFReadStreamSetProperty(rs, _kCFStreamPropertyWriteTimeout, nm);
+ CFReadStreamSetProperty(rs, _kCFStreamPropertySocketImmediateBufferTimeOut, nm);
+ CFRelease(nm);
+
CFDictionaryRef dr = SCDynamicStoreCopyProxies(NULL);
CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPProxy, dr);
CFRelease(dr);
Status("Connecting to %s", hs.c_str());
- if (!CFReadStreamOpen(rs)) {
- CfrsError("Open", rs);
- Fail(true);
+ switch (CFReadStreamOpen(rs, to)) {
+ case -1:
+ CfrsError("Open", rs);
+ goto fail;
+
+ case 0:
+ _error->Error("Host Unreachable");
+ cached.insert(hs);
+ goto fail;
+
+ case 1:
+ /* success */
+ break;
+
+ fail:
+ Fail(true);
goto done;
}
if (rd == -1) {
CfrsError(uri.Host.c_str(), rs);
+ cached.insert(hs);
Fail(true);
goto done;
}
}
}
+ if (sc < 200 || sc >= 300 && sc != 304) {
+ sr = CFHTTPMessageCopyResponseStatusLine(hm);
+
+ size_t ln = CFStringGetLength(sr) + 1;
+ char cr[ln];
+
+ if (!CFStringGetCString(sr, cr, ln, se)) {
+ Fail();
+ goto done;
+ }
+
+ CFRelease(sr);
+
+ _error->Error("%s", cr);
+
+ Fail();
+ goto done_;
+ }
+
CFRelease(hm);
if (sc == 304) {
Res.IMSHit = true;
Res.LastModified = Queue->LastModified;
URIDone(Res);
- } else if (sc < 200 || sc >= 300)
- Fail();
- else {
+ } else {
Hashes hash;
File = new FileFd(Queue->DestFile, FileFd::WriteAny);
int main()
{
-#if !defined(__ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__) || __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ < 10200
- struct nlist nl[2];
- memset(nl, 0, sizeof(nl));
- nl[0].n_un.n_name = (char *) "_useMDNSResponder";
- nlist("/usr/lib/libc.dylib", nl);
- if (nl[0].n_type != N_UNDF)
- *(int *) nl[0].n_value = 0;
-#endif
-
setlocale(LC_ALL, "");
+ // ignore SIGPIPE, this can happen on write() if the socket
+ // closes the connection (this is dealt with via ServerDie())
+ signal(SIGPIPE, SIG_IGN);
HttpMethod Mth;
CFRelease(plist);
}
- if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
- if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
- if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
- SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
- CFRelease(serial);
- }
+ if (UniqueID_ == NULL)
+ if (void *libMobileGestalt = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY))
+ if (CFStringRef (*$MGCopyAnswer)(CFStringRef) = (CFStringRef (*)(CFStringRef)) dlsym(libMobileGestalt, "MGCopyAnswer"))
+ UniqueID_ = $MGCopyAnswer(CFSTR("UniqueDeviceID"));
+
+ if (UniqueID_ == NULL)
+ if (void *lockdown = lockdown_connect()) {
+ UniqueID_ = lockdown_copy_value(lockdown, NULL, kLockdownUniqueDeviceIDKey);
+ lockdown_disconnect(lockdown);
+ }
- IOObjectRelease(service);
- }
-
return Mth.Loop();
}