diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.c b/CoreFoundation/URL.subproj/CFURLSessionInterface.c index 32f09c4c37..6226a3f5ce 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.c +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.c @@ -657,3 +657,22 @@ CFURLSessionSList *_Nullable CFURLSessionSListAppend(CFURLSessionSList *_Nullabl void CFURLSessionSListFreeAll(CFURLSessionSList *_Nullable list) { curl_slist_free_all((struct curl_slist *) list); } + +bool CFURLSessionCurlHostIsEqual(const char *_Nonnull url, const char *_Nonnull expectedHost) { +#if LIBCURL_VERSION_MAJOR > 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 62) + bool isEqual = false; + CURLU *h = curl_url(); + if (0 == curl_url_set(h, CURLUPART_URL, url, 0)) { + char *curlHost = NULL; + if (0 == curl_url_get(h, CURLUPART_HOST, &curlHost, 0)) { + isEqual = (strlen(curlHost) == strlen(expectedHost) && + strncmp(curlHost, expectedHost, strlen(curlHost)) == 0); + curl_free(curlHost); + } + curl_free(h); + } + return isEqual; +#else + return true; +#endif +} diff --git a/CoreFoundation/URL.subproj/CFURLSessionInterface.h b/CoreFoundation/URL.subproj/CFURLSessionInterface.h index 574250a88d..d760177041 100644 --- a/CoreFoundation/URL.subproj/CFURLSessionInterface.h +++ b/CoreFoundation/URL.subproj/CFURLSessionInterface.h @@ -642,6 +642,7 @@ typedef struct CFURLSessionSList CFURLSessionSList; CF_EXPORT CFURLSessionSList *_Nullable CFURLSessionSListAppend(CFURLSessionSList *_Nullable list, const char * _Nullable string); CF_EXPORT void CFURLSessionSListFreeAll(CFURLSessionSList *_Nullable list); +CF_EXPORT bool CFURLSessionCurlHostIsEqual(const char *_Nonnull url, const char *_Nonnull expectedHost); CF_EXTERN_C_END diff --git a/Sources/FoundationNetworking/URLSession/FTP/FTPURLProtocol.swift b/Sources/FoundationNetworking/URLSession/FTP/FTPURLProtocol.swift index 8f87e884cf..55583fd2b8 100644 --- a/Sources/FoundationNetworking/URLSession/FTP/FTPURLProtocol.swift +++ b/Sources/FoundationNetworking/URLSession/FTP/FTPURLProtocol.swift @@ -56,7 +56,21 @@ internal class _FTPURLProtocol: _NativeProtocol { easyHandle.set(debugOutputOn: enableLibcurlDebugOutput, task: task!) easyHandle.set(skipAllSignalHandling: true) guard let url = request.url else { fatalError("No URL in request.") } - easyHandle.set(url: url) + guard url.host != nil else { + self.internalState = .transferFailed + let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL, + userInfo: [NSLocalizedDescriptionKey: "FTP URL must have a host"]) + failWith(error: error, request: request) + return + } + do { + try easyHandle.set(url: url) + } catch { + self.internalState = .transferFailed + let nsError = error as? NSError ?? NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL) + failWith(error: nsError, request: request) + return + } easyHandle.set(preferredReceiveBufferSize: Int.max) do { switch (body, try body.getBodyLength()) { diff --git a/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift b/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift index 9695be004e..abf6623435 100644 --- a/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift +++ b/Sources/FoundationNetworking/URLSession/HTTP/HTTPURLProtocol.swift @@ -311,7 +311,21 @@ internal class _HTTPURLProtocol: _NativeProtocol { guard let url = request.url else { fatalError("No URL in request.") } - easyHandle.set(url: url) + guard url.host != nil else { + self.internalState = .transferFailed + let error = NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL, + userInfo: [NSLocalizedDescriptionKey: "HTTP URL must have a host"]) + failWith(error: error, request: request) + return + } + do { + try easyHandle.set(url: url) + } catch { + self.internalState = .transferFailed + let nsError = error as? NSError ?? NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL) + failWith(error: nsError, request: request) + return + } let session = task?.session as! URLSession let _config = session._configuration easyHandle.set(sessionConfig: _config) diff --git a/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift b/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift index d6e50554ac..4513cdb701 100644 --- a/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift +++ b/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift @@ -165,10 +165,16 @@ extension _EasyHandle { } /// URL to use in the request /// - SeeAlso: https://curl.haxx.se/libcurl/c/CURLOPT_URL.html - func set(url: URL) { + func set(url: URL) throws { _url = url - url.absoluteString.withCString { - try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionURL, UnsafeMutablePointer(mutating: $0)).asError() + try url.absoluteString.withCString { urlPtr in + try url.host?.withCString { hostPtr in + guard CFURLSessionCurlHostIsEqual(urlPtr, hostPtr) else { + throw NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL, + userInfo: [NSLocalizedDescriptionKey: "URLSession and curl did not agree on URL host"]) + } + } + try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionURL, UnsafeMutablePointer(mutating: urlPtr)).asError() } }