X-Git-Url: https://git.saurik.com/apt-legacy.git/blobdiff_plain/032f992c5ed1130c348c7ddd074cde239e09cc87..b62917f161e80c11d4c3265006102d8d2c1651c5:/methods/http.cc diff --git a/methods/http.cc b/methods/http.cc index 2819ae1..6de79fa 100644 --- a/methods/http.cc +++ b/methods/http.cc @@ -1,13 +1,9 @@ -extern "C" { - #include -} - // -*- 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. @@ -33,6 +29,7 @@ extern "C" { #include #include #include +#include #include #include @@ -44,26 +41,31 @@ extern "C" { #include #include #include +#include +#include #include + // Internet stuff #include #include +#include +#include #include #include #include +#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); @@ -111,7 +113,9 @@ int HttpMethod::FailFd = -1; 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; @@ -362,22 +366,27 @@ bool ServerState::Open() 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) @@ -421,8 +430,8 @@ bool ServerState::Close() /*}}}*/ // 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; @@ -598,7 +607,7 @@ bool ServerState::HeaderLine(string Line) // 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")); } @@ -606,7 +615,7 @@ bool ServerState::HeaderLine(string Line) { 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")); } @@ -681,10 +690,61 @@ bool ServerState::HeaderLine(string Line) 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(arg) = 1; + CFRunLoopStop(CFRunLoopGetCurrent()); + break; + + case kCFStreamEventErrorOccurred: + *reinterpret_cast(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 */ @@ -764,11 +824,14 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out) 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; @@ -953,7 +1016,9 @@ bool HttpMethod::ServerDie(ServerState *Srv) 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 @@ -965,6 +1030,27 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) 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) @@ -994,7 +1080,8 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv) 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 @@ -1049,7 +1136,6 @@ bool HttpMethod::Fetch(FetchItem *) // Queue the requests int Depth = -1; - bool Tail = false; for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; I = I->Next, Depth++) { @@ -1061,8 +1147,6 @@ bool HttpMethod::Fetch(FetchItem *) if (Server->Comp(I->Uri) == false) break; if (QueueBack == I) - Tail = true; - if (Tail == true) { QueueBack = I->Next; SendReq(I,Server->Out); @@ -1081,6 +1165,7 @@ bool HttpMethod::Configuration(string Message) 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); @@ -1094,10 +1179,16 @@ bool HttpMethod::Configuration(string Message) /* */ int HttpMethod::Loop() { + typedef vector StringVector; + typedef vector::iterator StringVectorIterator; + map Redirected; + signal(SIGTERM,SigTerm); signal(SIGINT,SigTerm); Server = 0; + + std::set cached; int FailCounter = 0; while (1) @@ -1125,19 +1216,24 @@ int HttpMethod::Loop() 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); @@ -1153,11 +1249,16 @@ int HttpMethod::Loop() 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_); @@ -1166,21 +1267,28 @@ int HttpMethod::Loop() 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); @@ -1197,9 +1305,22 @@ int HttpMethod::Loop() 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; } @@ -1207,6 +1328,7 @@ int HttpMethod::Loop() if (rd == -1) { CfrsError(uri.Host.c_str(), rs); + cached.insert(hs); Fail(true); goto done; } @@ -1288,6 +1410,25 @@ int HttpMethod::Loop() } } + 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) { @@ -1295,9 +1436,7 @@ int HttpMethod::Loop() 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); @@ -1386,16 +1525,10 @@ int HttpMethod::Loop() 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; @@ -1422,16 +1555,17 @@ int main() 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(); }