diff --git a/.github/workflows/build-lib-x64.yml b/.github/workflows/build-lib-x64.yml index 37a4aaf..8be4236 100644 --- a/.github/workflows/build-lib-x64.yml +++ b/.github/workflows/build-lib-x64.yml @@ -17,6 +17,20 @@ jobs: - name: Setup MSBuild uses: microsoft/setup-msbuild@v2 + - name: Verify dependency inputs + shell: pwsh + run: | + $dependencies = @( + @('curl/libcurl.lib', 'ce95938a2ab28c117c060d0d230627e4394f2816e6bf0278582f4cc16f5252ff', 'https://curl.se/download/curl-8.23.0.zip', '85477fe11408471285f38a601e7c6f53470d13be9a5a316919b071ad02e68213'), + @('curl/zlib.lib', 'cb8f44532898db105798fcfa2e063ead4b4488ff9806782a8d3d0f410014e8c6', 'https://zlib.net/zlib132.zip', 'e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28'), + @('libsodium/libsodium.lib', '975fe3ea19d4c78ae8b3ac12fc9212063fea7edadbb6307f089f52febdafa371', 'https://download.libsodium.org/libsodium/releases/libsodium-1.0.20-stable-msvc.zip', 'ebaa204fdfcedc51dc1ee1bbd03c8d552a14b3372f87f94e44c71a8533f77df4') + ) + foreach ($dependency in $dependencies) { + $actual = (Get-FileHash -LiteralPath $dependency[0] -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -cne $dependency[1]) { throw "Unexpected dependency binary: $($dependency[0])" } + Write-Output "Verified $($dependency[0]); upstream archive $($dependency[2]); source SHA256 $($dependency[3])" + } + - name: Build library_x64.lib (Release|x64) run: > msbuild .\library.vcxproj diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2521f0c..ea59513 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,26 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + with: + fetch-depth: 2 - name: setup msbuild uses: microsoft/setup-msbuild@v2 + - name: Verify dependency inputs + shell: pwsh + run: | + $dependencies = @( + @('curl/libcurl.lib', 'ce95938a2ab28c117c060d0d230627e4394f2816e6bf0278582f4cc16f5252ff', 'https://curl.se/download/curl-8.23.0.zip', '85477fe11408471285f38a601e7c6f53470d13be9a5a316919b071ad02e68213'), + @('curl/zlib.lib', 'cb8f44532898db105798fcfa2e063ead4b4488ff9806782a8d3d0f410014e8c6', 'https://zlib.net/zlib132.zip', 'e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28'), + @('libsodium/libsodium.lib', '975fe3ea19d4c78ae8b3ac12fc9212063fea7edadbb6307f089f52febdafa371', 'https://download.libsodium.org/libsodium/releases/libsodium-1.0.20-stable-msvc.zip', 'ebaa204fdfcedc51dc1ee1bbd03c8d552a14b3372f87f94e44c71a8533f77df4') + ) + foreach ($dependency in $dependencies) { + $actual = (Get-FileHash -LiteralPath $dependency[0] -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -cne $dependency[1]) { throw "Unexpected dependency binary: $($dependency[0])" } + Write-Output "Verified $($dependency[0]); upstream archive $($dependency[2]); source SHA256 $($dependency[3])" + } + - name: build (release x64) shell: pwsh run: | @@ -34,19 +50,336 @@ jobs: } } $source = Join-Path $env:RUNNER_TEMP 'security-regression.cpp' + $testHeader = Join-Path $env:RUNNER_TEMP 'auth-test-access.hpp' + $headerText = [IO.File]::ReadAllText((Join-Path $env:GITHUB_WORKSPACE 'auth.hpp')) + $headerText = $headerText.Replace("`tprivate:", "`tprivate:`n friend struct keyauth_test_access;") + [IO.File]::WriteAllText($testHeader, $headerText) @' #define SODIUM_STATIC - #include "auth.hpp" + #include "auth-test-access.hpp" + #include #include + #include + #include #include extern std::string curl_escape(CURL*, const std::string&); + extern std::string checksum(); extern std::string get_str_between_two_str(const std::string&, const std::string&, const std::string&); + namespace KeyAuth { + struct keyauth_test_access { + static bool damaged_binding_rejected(api& app) { + const auto saved = app.initialized_context_; + app.initialized_context_.clear(); + bool rejected = !app.authenticated() && request_context_rejected(app, app.get_url()); + app.initialized_context_ = saved; + app.initialized_context_[0] ^= 1; + rejected = rejected && !app.authenticated() && request_context_rejected(app, app.get_url()); + app.initialized_context_ = saved; + return rejected && app.authenticated(); + } + static bool request_context_rejected(api& app, const std::string& url) { + try { app.req("type=check", url); } + catch (const request_error& error) { + const std::string message = error.what(); + return message.find("Application configuration changed") == 0 || + message.find("Request endpoint does not match") == 0; + } + return false; + } + static bool response_paid(api& app, const nlohmann::json& data) { + app.load_response_data(data); + return app.response.isPaid; + } + static bool policy_snapshot_valid(api& app) { + std::lock_guard lock(app.api_mutex_); + if (app.allowed_hosts.size() > 2 || app.pinned_public_keys.size() > 2) return false; + for (const auto& host : app.allowed_hosts) + if (host != "one.example" && host != "two.example") return false; + for (const auto& pin : app.pinned_public_keys) + if (pin != "sha256//one" && pin != "sha256//two") return false; + return true; + } + static void establish(api& app, bool paid, const std::string& expiry) { + app.initialized = true; + app.bind_initialization_context(); + app.sessionid = "regression-session"; + app.user_data.username = "regression-user"; + app.user_data.subscriptions = {{"default", expiry}}; + app.response.success = true; + app.response.isPaid = paid; + app.mark_authenticated(); + } + static void renew(api& app, bool paid) { + app.response.isPaid = paid; + app.refresh_auth_runtime(); + } + static bool rejects_modified_state(api& app) { + const auto saved = app.enckey; + app.enckey[0] ^= 1; + const bool rejected = !app.authenticated(); + app.enckey = saved; + return rejected; + } + static bool rejects_modified_key(api& app) { + const auto saved = app.auth_seal_key_; + app.auth_seal_key_[0] ^= 1; + const bool rejected = !app.authenticated(); + app.auth_seal_key_ = saved; + return rejected; + } + static bool rejects_copied_state(api& from, api& to) { + to.initialized = from.initialized; + to.LoggedIn.store(true); + to.sessionid = from.sessionid; + to.initialized_context_ = from.initialized_context_; + to.user_data = from.user_data; + to.enckey = from.enckey; + to.auth_seal_key_ = from.auth_seal_key_; + to.auth_nonce_.store(from.auth_nonce_.load()); + to.auth_window_.store(from.auth_window_.load()); + to.auth_seal_.store(from.auth_seal_.load()); + return !to.authenticated(); + } + static bool stale_state_can_renew(api& app) { + const auto current = keyauth_detail::auth_window_now(); + const auto stale = current > 3 ? current - 3 : -1; + app.auth_window_.store(stale); + app.auth_seal_.store(app.compute_auth_seal(app.auth_nonce_.load(), stale)); + if (app.authenticated() || !app.local_auth_valid(false, false)) return false; + renew(app, false); + return app.authenticated(); + } + static bool reset_wipes_state(api& app) { + app.reset_auth_runtime(); + return !app.authenticated() && app.enckey.empty() && + std::all_of(app.auth_seal_key_.begin(), app.auth_seal_key_.end(), + [](unsigned char value) { return value == 0; }); + } + }; + } + namespace authentication_state_test { + int run() { + const auto fail = [](int line) { + std::cerr << "Authentication state regression failed at line " << line + << "; current window " << keyauth_detail::auth_window_now() << "\n"; + return 1; + }; + if (keyauth_detail::auth_window_for_seconds(0) != 1 || + keyauth_detail::auth_window_for_seconds(89) != 1 || + keyauth_detail::auth_window_for_seconds(90) != 2 || + keyauth_detail::auth_window_for_seconds(-1) != 0) return fail(__LINE__); + using access = KeyAuth::keyauth_test_access; + KeyAuth::api app("app", "1234567890", "1.0", "https://keyauth.win/api/1.3/", ""); + KeyAuth::api other("app", "1234567890", "1.0", "https://keyauth.win/api/1.3/", ""); + auto role_reply = nlohmann::json::parse(R"({"success":true,"message":"ok"})"); + for (const auto& role : {nlohmann::json(nullptr), nlohmann::json(1), nlohmann::json(true), + nlohmann::json::array(), nlohmann::json::object(), nlohmann::json(""), + nlohmann::json(" "), nlohmann::json("tester"), nlohmann::json("not_checked")}) { + role_reply["role"] = role; + if (access::response_paid(app, role_reply)) return fail(__LINE__); + } + role_reply["role"] = "premium"; + if (!access::response_paid(app, role_reply)) return fail(__LINE__); + role_reply["success"] = false; + if (access::response_paid(app, role_reply)) return fail(__LINE__); + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + access::establish(app, false, "1"); + if (!app.authenticated() || app.authenticated(true)) return fail(__LINE__); + app.response.isPaid = true; + app.user_data.subscriptions = {{"default", std::to_string(now + 3600)}}; + if (app.authenticated(true)) return fail(__LINE__); + access::renew(app, false); + if (app.authenticated(true)) return fail(__LINE__); + access::renew(app, true); + if (!app.authenticated(true)) return fail(__LINE__); + app.response.isPaid = false; + if (!app.authenticated(true)) return fail(__LINE__); + if (!access::rejects_modified_state(app) || !access::rejects_modified_key(app)) return fail(__LINE__); + if (!access::rejects_copied_state(app, other)) return fail(__LINE__); + access::establish(app, false, std::to_string(now + 3600)); + app.user_data.subscriptions.clear(); + if (!app.authenticated(true)) return fail(__LINE__); + if (!access::stale_state_can_renew(app) || !app.authenticated(true)) return fail(__LINE__); + access::establish(app, false, std::to_string(now + 3600) + "invalid"); + if (app.authenticated(true)) return fail(__LINE__); + app.name = "prefix|suffix"; + access::establish(app, false, "1"); + app.ownerid = "1234567890|prefix"; + app.name = "suffix"; + if (app.authenticated()) return fail(__LINE__); + app.ownerid = "1234567890"; + app.name = "prefix|suffix"; + if (!app.authenticated()) return fail(__LINE__); + if (!access::reset_wipes_state(app)) return fail(__LINE__); + std::thread hosts([&] { + for (int i = 0; i < 250; ++i) { + app.set_allowed_hosts({"one.example"}); + app.add_allowed_host("two.example"); + app.clear_allowed_hosts(); + } + }); + std::thread pins([&] { + for (int i = 0; i < 250; ++i) { + app.set_pinned_public_keys({"sha256//one"}); + app.add_pinned_public_key("sha256//two"); + app.clear_pinned_public_keys(); + } + }); + for (int i = 0; i < 250; ++i) + if (!access::policy_snapshot_valid(app)) std::abort(); + hosts.join(); + pins.join(); + if (!access::policy_snapshot_valid(app)) return fail(__LINE__); + std::cout << "Authentication state tampering and concurrent policy regression tests passed\n"; + return 0; + } + } + namespace application_binding_test { + int run() { + using namespace keyauth_detail; + using access = KeyAuth::keyauth_test_access; + const auto fail = [](const char* message) { std::cerr << "Binding regression: " << message << "\n"; return 1; }; + const std::string original = "https://auth.example.invalid/api/1.3/?channel=stable"; + KeyAuth::api app("app", "1234567890", "1.0", original, ""); + access::establish(app, false, "1"); + if (!app.authenticated()) return fail("initial state"); + if (!access::damaged_binding_rejected(app)) return fail("damaged binding"); + app.name = "other"; + if (app.authenticated() || !access::request_context_rejected(app, original)) return fail("app swap"); + app.name = "app"; + app.ownerid = "0987654321"; + if (app.authenticated() || !access::request_context_rejected(app, original)) return fail("owner swap"); + app.ownerid = "1234567890"; + app.url = "https://auth.example.invalid/other/"; + if (app.authenticated() || !access::request_context_rejected(app, app.url)) return fail("path swap"); + app.url = original; + if (!access::request_context_rejected(app, "https://other.example.invalid/api/1.3/")) return fail("request redirect"); + app.url = "HTTPS://AUTH.EXAMPLE.INVALID.:443/api/1.3/?channel=stable"; + if (!app.authenticated()) return fail("equivalent URL"); + app.enable_secure_strings(); + if (!app.authenticated()) return fail("protected settings"); + app.enable_secure_strings(false); + if (!app.authenticated()) return fail("restored settings"); + const auto base = parse_api_endpoint(original).url; + if (!request_endpoint_matches(base, base, "login") || + !request_endpoint_matches(base, base + "&check_paid=1", "check") || + request_endpoint_matches(base, base + "&check_paid=1", "login") || + request_endpoint_matches(base, base + "&appname=other", "check")) return fail("query handling"); + const auto plain = parse_api_endpoint("https://auth.example.invalid/api/1.3/").url; + if (!request_endpoint_matches(plain, plain + "?check_paid=1", "check")) return fail("plain paid query"); + auto reply = nlohmann::json::parse(R"({"success":true,"ownerid":"owner","appname":"app","sessionid":"session"})"); + validate_response_context(reply, "owner", "app", "session", "login"); + validate_response_context(nlohmann::json::parse(R"({"success":true})"), "owner", "app", "session", "login"); + for (const char* field : {"ownerid", "appname", "sessionid"}) { + for (const auto& invalid : {nlohmann::json("other"), nlohmann::json(1), nlohmann::json(nullptr)}) { + auto changed = reply; + changed[field] = invalid; + bool rejected = false; + try { validate_response_context(changed, "owner", "app", "session", "login"); } + catch (const std::invalid_argument&) { rejected = true; } + if (!rejected) return fail("response swap or invalid identifier"); + } + } + reply["sessionid"] = "new-session"; + validate_response_context(reply, "owner", "app", "", "init"); + reply["sessionid"] = ""; + validate_response_context(reply, "owner", "app", "session", "logout"); + bool rejected = false; + try { validate_response_context(reply, "owner", "app", "", "init"); } + catch (const std::invalid_argument&) { rejected = true; } + if (!rejected) return fail("empty initialization session"); + validate_response_context(nlohmann::json::parse(R"({"success":false,"sessionid":null,"appname":null})"), + "owner", "app", "session", "check"); + std::cout << "Application identity and signed response context regression tests passed\n"; + return 0; + } + } + namespace string_lifetime_test { + int run() { + constexpr auto literal = XorStr("compile-time"); + static_assert(sizeof(literal) > 0, "String wrapper must remain a literal type."); + const auto check = [](auto wrapped, const auto& expected) { + using String = std::decay_t; + static_assert(std::is_standard_layout_v); + const size_t bytes = (expected.size() + 1) * sizeof(typename String::value_type); + std::vector before(bytes); + std::memcpy(before.data(), &wrapped, bytes); + if (wrapped.str() != expected || std::memcmp(before.data(), &wrapped, bytes) != 0) return false; + const auto* view = wrapped.c_str(); + if (String(view) != expected || wrapped.str() != expected || String(view) != expected) return false; + return true; + }; + if (!check(XorStr("temporary-secret-marker"), std::string("temporary-secret-marker")) || + !check(XorStr(L"wide-marker"), std::wstring(L"wide-marker")) || + !check(XorStr(u"utf16-marker"), std::u16string(u"utf16-marker")) || + !check(XorStr(U"utf32-marker"), std::u32string(U"utf32-marker")) || + !check(XorStr(""), std::string())) return 1; + auto first = XorStr("same"); + auto second = XorStr("same"); + if (!(first == second) || !(std::string("same") == first) || + (first + second) != "samesame" || (std::string("x") + first) != "xsame") return 1; + std::ostringstream stream; + stream << first; + if (stream.str() != "same") return 1; + std::cout << "String conversion plaintext lifetime regression tests passed\n"; + return 0; + } + } + namespace local_data_test { + int run() { + using KeyAuth::api; + std::string binary; + for (int i = 0; i < 256; ++i) binary.push_back(static_cast(i)); + for (const auto& plain : {std::string(), std::string("saved-passphrase"), binary}) { + const auto protected_value = api::protect_data(plain); + if (protected_value.rfind("keyauth:dpapi:v1:", 0) != 0 || + api::unprotect_data(protected_value) != plain) return 1; + } + const auto protected_value = api::protect_data("secret"); + for (const auto& invalid : {std::string("plaintext"), std::string("keyauth:dpapi:v2:00"), + protected_value + "z", protected_value.substr(0, protected_value.size() - 2)}) { + bool rejected = false; + try { api::unprotect_data(invalid); } catch (const KeyAuth::request_error&) { rejected = true; } + if (!rejected) return 1; + } + std::string malformed = std::string("KA-DATA1payload") + std::string(crypto_generichash_BYTES, '\0'); + DATA_BLOB input{static_cast(malformed.size()), reinterpret_cast(malformed.data())}; + DATA_BLOB output{}; + if (!CryptProtectData(&input, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_UI_FORBIDDEN, &output)) return 1; + std::string encoded("keyauth:dpapi:v1:"); + const char hex[] = "0123456789abcdef"; + for (DWORD i = 0; i < output.cbData; ++i) { + encoded.push_back(hex[output.pbData[i] >> 4]); + encoded.push_back(hex[output.pbData[i] & 15]); + } + SecureZeroMemory(output.pbData, output.cbData); + LocalFree(output.pbData); + bool rejected = false; + try { api::unprotect_data(encoded); } catch (const KeyAuth::request_error&) { rejected = true; } + if (!rejected) return 1; + std::cout << "Account-bound local data encryption regression tests passed\n"; + return 0; + } + } namespace response_headers_test { static void require(bool condition) { if (!condition) std::abort(); } int run() { + const int previous_mode = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + const std::string previous_locale = std::setlocale(LC_CTYPE, nullptr); + for (const char* locale : {"C", "tr-TR", "ja-JP", "zh-CN", "pt-BR"}) { + if (!std::setlocale(LC_CTYPE, locale)) return 1; + keyauth_detail::SignatureHeaders localized; + keyauth_detail::consume_header(localized, "X-SIGNATURE-ED25519: valid\r\n"); + keyauth_detail::consume_header(localized, "X-SIGNATURE-TIMESTAMP: 1770000000\r\n"); + if (localized.signature != "valid" || localized.timestamp != "1770000000") return 1; + if (keyauth_detail::ascii_lower('I') != 'i') return 1; + } + std::setlocale(LC_CTYPE, previous_locale.c_str()); + _configthreadlocale(previous_mode); keyauth_detail::SignatureHeaders h; using keyauth_detail::consume_header; consume_header(h, "HTTP/1.1 200 Connection established\r\n"); @@ -56,12 +389,33 @@ jobs: consume_header(h, "X-SIGNATURE-ED25519:\tvalid \t\r\n"); consume_header(h, "x-signature-timestamp: 1770000000\r\n"); require(h.signature == "valid" && h.timestamp == "1770000000"); + require(h.timestamp_received_at > 0); + const auto observed_at = h.timestamp_received_at; + consume_header(h, "x-signature-timestamp: 1770000000\r\n"); + require(h.timestamp_received_at == observed_at); consume_header(h, "x-signature-ed25519: valid\r\n"); require(!h.conflicting); consume_header(h, "x-signature-ed25519: other\r\n"); require(h.conflicting); consume_header(h, "HTTP/1.1 103 Early Hints\r\n"); require(!h.conflicting && h.signature.empty()); + consume_header(h, "x-signature-ed25519: \r\n"); + consume_header(h, "x-signature-ed25519: interim\r\n"); + require(h.conflicting); + consume_header(h, "HTTP/2 200\r\n"); + require(!h.signature_seen && !h.timestamp_seen && !h.conflicting); + consume_header(h, "x-signature-timestamp: \r\n"); + consume_header(h, "x-signature-timestamp: 1770000000\r\n"); + require(h.conflicting); + consume_header(h, "HTTP/2 200\r\n"); + consume_header(h, "x-signature-ed25519: same\r\n"); + consume_header(h, "x-signature-ed25519: \r\n"); + require(h.conflicting); + consume_header(h, "HTTP/2 200\r\n"); + consume_header(h, "x-signature-timestamp: 1770000000\r\n"); + consume_header(h, "x-signature-timestamp: 1770000000\r\n"); + require(!h.conflicting); + consume_header(h, "HTTP/2 200\r\n"); consume_header(h, "x-signature-ed25519: interim\r\n"); consume_header(h, "HTTP/1.1 503 Service Unavailable\r\n"); require(h.signature.empty()); @@ -119,6 +473,27 @@ jobs: namespace file_digest_test { int run() { + // Cover Unicode paths without requiring English language packs. -nigel + const auto directory = std::filesystem::temp_directory_path() / + std::filesystem::path(L"keyauth-\u4e2d\u6587-\u65e5\u672c\u8a9e-a\u00e7\u00e3o"); + std::filesystem::create_directories(directory); + const auto unicode_path = directory / L"\u30c6\u30b9\u30c8-\u00e7.bin"; + { std::ofstream output(unicode_path, std::ios::binary); output << "abc"; } + const auto decoded_path = keyauth_detail::native_file_path(unicode_path.u8string()); + const auto unicode_digest = keyauth_detail::file_md5(decoded_path); + std::filesystem::remove(unicode_path); + std::filesystem::remove(directory); + if (unicode_digest != "900150983cd24fb0d6963f7d28e17f72") return 1; + bool invalid_path_rejected = false; + try { keyauth_detail::native_file_path(std::string("a\0b", 3)); } + catch (const std::invalid_argument&) { invalid_path_rejected = true; } + if (!invalid_path_rejected) return 1; + std::wstring executable(32768, L'\0'); + const DWORD length = GetModuleFileNameW(nullptr, executable.data(), + static_cast(executable.size())); + if (!length || length >= executable.size()) return 1; + executable.resize(length); + if (checksum() != keyauth_detail::file_md5(std::filesystem::path(executable))) return 1; const auto path = std::filesystem::temp_directory_path() / "keyauth-checksum-test.bin"; { std::ofstream out(path, std::ios::binary); out << "abc"; } const auto digest = keyauth_detail::file_md5(path); @@ -150,15 +525,48 @@ jobs: namespace api_lifecycle_test { int run() { + const wchar_t query[] = {L'?', L'u', L'=', L'x'}; + if (keyauth_detail::bounded_http_query(query, sizeof(query), query, sizeof(query)) != L"?u=x") return 1; + if (keyauth_detail::bounded_http_query(query + 1, 2 * sizeof(wchar_t), query, sizeof(query)) != L"u=") return 1; + if (!keyauth_detail::bounded_http_query(nullptr, 0, query, sizeof(query)).empty()) return 1; + auto rejects = [&](const wchar_t* value, size_t bytes) { + try { keyauth_detail::bounded_http_query(value, bytes, query, sizeof(query)); } + catch (const std::invalid_argument&) { return true; } + return false; + }; + if (!rejects(nullptr, 2) || !rejects(query, 1) || !rejects(query, sizeof(query) + 2) || + !rejects(query + 4, 2) || !rejects(reinterpret_cast(reinterpret_cast(query) + 1), 2)) return 1; + const wchar_t embedded[] = {L'?', L'\0', L'x'}; + bool rejected = false; + try { keyauth_detail::bounded_http_query(embedded, sizeof(embedded), embedded, sizeof(embedded)); } + catch (const std::invalid_argument&) { rejected = true; } + if (!rejected) return 1; + if (keyauth_detail::http_reason("login denied") != "login denied" || + keyauth_detail::http_reason(std::string("a\r\n\0b", 5)) != "a b" || + keyauth_detail::http_reason(std::string(70000, 'x')).size() != USHRT_MAX) return 1; + std::cout << "Local HTTP query and reason bounds regression tests passed\n"; if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) return 1; + const auto transport = curl_version_info(CURLVERSION_NOW); + if (!transport || transport->version_num < 0x081700 || !transport->libz_version || + std::string(transport->libz_version) != "1.3.2") return 1; + std::cout << "Linked transport: " << curl_version() << "; sodium: " << sodium_version_string() << "\n"; + KeyAuth::api skipped("skipped", "1234567890", "1.0", "https://keyauth.win/api/1.3/", ""); + skipped.response.success = true; + skipped.user_data.username = "forged"; + skipped.response.isPaid = true; + if (skipped.authenticated() || skipped.authenticated(true)) return 1; const auto hwid = utils::get_hwid(); if (hwid.rfind("S-1-", 0) != 0 || utils::get_hwid() != hwid) return 1; if (curl_escape(nullptr, "a&+= %") != "a%26%2B%3D%20%25") return 1; + const std::string binary("a\0b", 3); + if (curl_escape(nullptr, binary) != "a%00b" || binary != std::string("a\0b", 3)) return 1; + if (!curl_escape(nullptr, "").empty()) return 1; if (get_str_between_two_str("?user=test&token=abc", "&token=", "") != "abc") return 1; for (int i = 0; i < 10; ++i) { KeyAuth::api first("first", "1234567890", "1.0", "https://keyauth.win/api/1.3/", ""); KeyAuth::api second("second", "0987654321", "1.0", "https://keyauth.win/api/1.3/", ""); if (!first.require_signed_downloads) return 1; + if (first.max_download_response_bytes != 0) return 1; first.response.success = true; if (first.authenticated() || first.authenticated(true)) return 1; if (!keyauth_detail::auth_window_fresh(10, 10) || @@ -272,12 +680,33 @@ jobs: ResponseBuffer buffer{{}, 4}; require(append_response(buffer, "test", 4)); require(!append_response(buffer, "!", 1) && buffer.exceeded && buffer.body == "test"); + ResponseBuffer unlimited{{}, 0}; + const std::string chunk(1024 * 1024, 'x'); + for (int i = 0; i < 9; ++i) require(append_response(unlimited, chunk.data(), chunk.size())); + require(unlimited.body.size() == 9 * chunk.size() && !unlimited.exceeded); + require(decode_download("").empty()); + require(decode_download("00aAFf") == std::vector({0, 170, 255})); + std::string all_bytes; + const char* digits = "0123456789abcdef"; + for (int i = 0; i < 256; ++i) { all_bytes += digits[i >> 4]; all_bytes += digits[i & 15]; } + const auto decoded = decode_download(all_bytes); + require(decoded.size() == 256); + for (int i = 0; i < 256; ++i) require(decoded[i] == i); + for (const auto& invalid : {std::string("0"), std::string("zz"), std::string("00\0f", 4)}) { + bool rejected = false; + try { decode_download(invalid); } catch (const std::invalid_argument&) { rejected = true; } + require(rejected); + } require(!connection_address_allowed("127.0.0.1", 8080, true, true, false)); std::cout << "Email flow and bounded-response regression tests passed\n"; return 0; } } int main() { + if (application_binding_test::run() != 0) return 1; + if (string_lifetime_test::run() != 0) return 1; + if (local_data_test::run() != 0) return 1; + if (authentication_state_test::run() != 0) return 1; if (email_api_test::run() != 0) return 1; if (network_policy_test::run() != 0) return 1; if (response_headers_test::run() != 0) return 1; @@ -290,11 +719,136 @@ jobs: '@ | Set-Content -LiteralPath $source $object = Join-Path $env:RUNNER_TEMP 'security-regression.obj' $executable = Join-Path $env:RUNNER_TEMP 'security-regression.exe' - & cl.exe /nologo /std:c++17 /EHsc /MD "/I$env:GITHUB_WORKSPACE" "/I$env:GITHUB_WORKSPACE\libsodium" $source "/Fo$object" "/Fe$executable" /link "$env:GITHUB_WORKSPACE\x64\Release\library_x64.lib" "$env:GITHUB_WORKSPACE\curl\zlib.lib" user32.lib shell32.lib + & cl.exe /nologo /std:c++17 /EHsc /guard:cf /guard:ehcont /MD "/I$env:GITHUB_WORKSPACE" "/I$env:GITHUB_WORKSPACE\libsodium" $source "/Fo$object" "/Fe$executable" /link /GUARD:CF /GUARD:EHCONT /CETCOMPAT "$env:GITHUB_WORKSPACE\x64\Release\library_x64.lib" "$env:GITHUB_WORKSPACE\curl\zlib.lib" user32.lib shell32.lib if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } & $executable if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + function Assert-ImportTable([byte[]]$image) { + $pe = [BitConverter]::ToInt32($image, 60) + $optional = $pe + 24 + $optionalSize = [BitConverter]::ToUInt16($image, $pe + 20) + if ($optionalSize -lt 216 -or $optional -gt $image.Length - $optionalSize -or [BitConverter]::ToUInt16($image, $optional) -ne 0x20b) { throw 'Invalid x64 optional header.' } + if ([BitConverter]::ToUInt32($image, $optional + 108) -lt 13) { throw 'Import address directory is missing.' } + $iat = [uint64][BitConverter]::ToUInt32($image, $optional + 208) + $size = [uint64][BitConverter]::ToUInt32($image, $optional + 212) + if ($iat -eq 0 -or $size -lt 8 -or ($iat % 8) -ne 0 -or ($size % 8) -ne 0) { throw 'Invalid import address directory.' } + $count = [BitConverter]::ToUInt16($image, $pe + 6) + $table = $optional + $optionalSize + for ($i = 0; $i -lt $count; $i++) { + $section = $table + 40 * $i + $start = [uint64][BitConverter]::ToUInt32($image, $section + 12) + $length = [uint64][Math]::Max([BitConverter]::ToUInt32($image, $section + 8), [BitConverter]::ToUInt32($image, $section + 16)) + if ($iat -ge $start -and $iat + $size -le $start + $length) { + $flags = [BitConverter]::ToUInt32($image, $section + 36) + if (($flags -band 0x40000000) -eq 0 -or ($flags -band 2684354560) -ne 0) { throw 'Import address table must be readable, non-writable and non-executable.' } + return $section + } + } + throw 'Import address table is outside image sections.' + } + + function Assert-CodeSections([byte[]]$image) { + if ($image.Length -lt 64 -or [BitConverter]::ToUInt16($image, 0) -ne 0x5a4d) { throw 'Invalid DOS header.' } + $pe = [BitConverter]::ToInt32($image, 60) + if ($pe -lt 64 -or $pe -gt $image.Length - 24 -or [BitConverter]::ToUInt32($image, $pe) -ne 0x4550) { throw 'Invalid PE header.' } + $count = [BitConverter]::ToUInt16($image, $pe + 6) + $table = $pe + 24 + [BitConverter]::ToUInt16($image, $pe + 20) + if ($count -eq 0 -or $table -gt $image.Length - 40 * $count) { throw 'Invalid section table.' } + $text = -1 + for ($i = 0; $i -lt $count; $i++) { + $section = $table + 40 * $i + $name = [Text.Encoding]::ASCII.GetString($image, $section, 8).TrimEnd([char]0) + $flags = [BitConverter]::ToUInt32($image, $section + 36) + if (($flags -band 0x20000000) -and ($flags -band 2147483648)) { throw 'Executable section is writable.' } + if ($name -eq '.text') { + if (($flags -band 0x60000000) -ne 0x60000000) { throw '.text must be readable and executable.' } + $text = $section + } + } + if ($text -lt 0) { throw '.text section was not found.' } + return $text + } + $image = [IO.File]::ReadAllBytes($executable) + $text = Assert-CodeSections $image + $iatSection = Assert-ImportTable $image + foreach ($unsafeFlag in @([uint64]2147483648, [uint64]536870912)) { + $invalidImports = [byte[]]$image.Clone() + $flags = [uint32]([uint64][BitConverter]::ToUInt32($invalidImports, $iatSection + 36) -bor $unsafeFlag) + [BitConverter]::GetBytes($flags).CopyTo($invalidImports, $iatSection + 36) + $rejected = $false + try { Assert-ImportTable $invalidImports | Out-Null } catch { $rejected = $true } + if (-not $rejected) { throw 'Unsafe import address table permissions were not detected.' } + } + Write-Output 'Read-only, non-executable import table checks passed' + $invalid = [byte[]]$image.Clone() + $flags = [uint32]([uint64][BitConverter]::ToUInt32($invalid, $text + 36) -bor 2147483648) + [BitConverter]::GetBytes($flags).CopyTo($invalid, $text + 36) + $rejected = $false + try { Assert-CodeSections $invalid | Out-Null } catch { $rejected = $true } + if (-not $rejected) { throw 'Writable executable section regression was not detected.' } + Write-Output 'Executable section hardening regression tests passed' + + $previousHeader = & git show 'HEAD^:auth.hpp' + if ($LASTEXITCODE -ne 0) { throw 'Previous public header could not be read.' } + $previousHeader | Set-Content -LiteralPath (Join-Path $env:RUNNER_TEMP 'auth-previous.hpp') + $abiSource = Join-Path $env:RUNNER_TEMP 'abi-regression.cpp' + @' + #define SODIUM_STATIC + #include "auth-previous.hpp" + #include + #include + int main() { + alignas(KeyAuth::api) unsigned char memory[sizeof(KeyAuth::api) + 32]; + std::memset(memory, 0xa5, sizeof(memory)); + auto* app = new (memory) KeyAuth::api("app", "1234567890", "1.0", "https://keyauth.win/api/1.3/", ""); + app->enable_secure_strings(); + if (!app->name.empty()) return 1; + app->enable_secure_strings(false); + if (app->name != "app" || app->authenticated()) return 2; + app->response.success = true; + app->response.isPaid = true; + if (app->authenticated(true)) return 3; + app->~api(); + for (size_t i = sizeof(KeyAuth::api); i < sizeof(memory); ++i) + if (memory[i] != 0xa5) return 4; + std::cout << "Previous-header consumer compatibility passed; object size " << sizeof(KeyAuth::api) << "\n"; + return 0; + } + '@ | Set-Content -LiteralPath $abiSource + $abiObject = Join-Path $env:RUNNER_TEMP 'abi-regression.obj' + $abiExecutable = Join-Path $env:RUNNER_TEMP 'abi-regression.exe' + & cl.exe /nologo /std:c++17 /EHsc /guard:cf /guard:ehcont /MD /DNDEBUG "/I$env:GITHUB_WORKSPACE" "/I$env:GITHUB_WORKSPACE\libsodium" $abiSource "/Fo$abiObject" "/Fe$abiExecutable" /link /GUARD:CF /GUARD:EHCONT /CETCOMPAT "$env:GITHUB_WORKSPACE\x64\Release\library_x64.lib" "$env:GITHUB_WORKSPACE\curl\zlib.lib" user32.lib shell32.lib + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & $abiExecutable + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + $xorHeader = [IO.File]::ReadAllText((Join-Path $env:GITHUB_WORKSPACE 'xorstr.hpp')) + foreach ($variant in @(1, 2)) { + $time = if ($variant -eq 1) { '"00:00:01"' } else { '"00:00:02"' } + [IO.File]::WriteAllText((Join-Path $env:RUNNER_TEMP "xor-key$variant.hpp"), $xorHeader.Replace('__TIME__', $time)) + $unit = '#include "xor-key' + $variant + '.hpp"' + "`n" + 'std::string xor_text_' + $variant + '() { return XorStr("linked-string-marker").str(); }' + $unit | Set-Content -LiteralPath (Join-Path $env:RUNNER_TEMP "xor-unit$variant.cpp") + } + @' + #include + #include + std::string xor_text_1(); + std::string xor_text_2(); + int main() { + if (xor_text_1() != "linked-string-marker" || xor_text_2() != "linked-string-marker") return 1; + std::cout << "Mixed-key translation unit string regression tests passed\n"; + return 0; + } + '@ | Set-Content -LiteralPath (Join-Path $env:RUNNER_TEMP 'xor-main.cpp') + Push-Location $env:RUNNER_TEMP + try { + & cl.exe /nologo /std:c++17 /EHsc /MD /Od xor-unit1.cpp xor-unit2.cpp xor-main.cpp /Fe:xor-mixed.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & ./xor-mixed.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + } finally { Pop-Location } + - name: collect build output shell: pwsh run: | diff --git a/auth.cpp b/auth.cpp index 747c0dd..b8d8d4b 100644 --- a/auth.cpp +++ b/auth.cpp @@ -16,6 +16,7 @@ #include #include +#include #pragma comment(lib, "crypt32.lib") #include #include @@ -43,6 +44,8 @@ #pragma comment(lib, "psapi.lib") #pragma comment(lib, "wintrust.lib") #pragma comment(lib, "dnsapi.lib") +#pragma comment(lib, "secur32.lib") +#pragma comment(lib, "iphlpapi.lib") #include #include @@ -93,9 +96,8 @@ #define SHA256_HASH_SIZE 32 -static std::string hexDecode(const std::string& hex); std::string get_str_between_two_str(const std::string& s, const std::string& start_delim, const std::string& stop_delim); -int VerifyPayload(std::string signature, std::string timestamp, std::string body); +int VerifyPayload(const std::string& signature, const std::string& timestamp, const std::string& body, long long received_at = 0); std::string checksum(); [[noreturn]] void error(std::string message); @@ -166,7 +168,7 @@ static bool normalize_signature_header(std::string sig_in, std::string& sig_hex_ if (is_hex_string(sig_in) && sig_in.size() == 128) { std::transform(sig_in.begin(), sig_in.end(), sig_in.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return keyauth_detail::ascii_lower(c); }); sig_hex_out = sig_in; return true; } @@ -236,6 +238,103 @@ static inline void securewipe(std::string& value) noexcept secure_zero(value); } +struct LocalHttpResources final { + bool initialized = false; + HTTP_SERVER_SESSION_ID session = 0; + HTTP_URL_GROUP_ID group = 0; + HANDLE queue = nullptr; + LocalHttpResources() = default; + LocalHttpResources(const LocalHttpResources&) = delete; + LocalHttpResources& operator=(const LocalHttpResources&) = delete; + ~LocalHttpResources() noexcept { + if (group) HttpCloseUrlGroup(group); + if (queue) HttpCloseRequestQueue(queue); + if (session) HttpCloseServerSession(session); + if (initialized) HttpTerminate(HTTP_INITIALIZE_SERVER, nullptr); + } +}; + +struct ScopeWipe final { + std::string* value; + explicit ScopeWipe(std::string& v) noexcept : value(&v) {} + ~ScopeWipe() noexcept { securewipe(*value); } +}; + +static std::string transform_saved_data(const std::string& input, bool encrypt) +{ + if (input.size() > MAXDWORD) throw KeyAuth::request_error("Local data exceeds the Windows protection limit."); + DATA_BLOB in{static_cast(input.size()), + reinterpret_cast(const_cast(input.data()))}; + struct Output final { + DATA_BLOB blob{}; + ~Output() { + if (blob.pbData) { + SecureZeroMemory(blob.pbData, blob.cbData); + LocalFree(blob.pbData); + } + } + } out; + const BOOL ok = encrypt ? + CryptProtectData(&in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_UI_FORBIDDEN, &out.blob) : + CryptUnprotectData(&in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_UI_FORBIDDEN, &out.blob); + if (!ok) throw KeyAuth::request_error("Windows could not protect or unlock local data for this account."); + if (!out.blob.cbData) return {}; + return std::string(reinterpret_cast(out.blob.pbData), out.blob.cbData); +} + +std::string KeyAuth::api::protect_data(const std::string& data) +{ + constexpr size_t digest_size = crypto_generichash_BYTES; + if (data.size() > MAXDWORD - 8 - digest_size) + throw request_error("Local data exceeds the Windows protection limit."); + if (sodium_init() < 0) throw request_error("Local data integrity initialization failed."); + std::string plain("KA-DATA1", 8); + ScopeWipe wipe_plain(plain); + plain += data; + const auto digest_offset = plain.size(); + plain.resize(digest_offset + digest_size); + if (crypto_generichash(reinterpret_cast(plain.data() + digest_offset), digest_size, + reinterpret_cast(data.data()), data.size(), nullptr, 0) != 0) + throw request_error("Local data integrity calculation failed."); + std::string encrypted = transform_saved_data(plain, true); + ScopeWipe wipe_encrypted(encrypted); + const std::string prefix = "keyauth:dpapi:v1:"; + if (encrypted.size() > (std::string().max_size() - prefix.size()) / 2) + throw request_error("Protected local data exceeds the string limit."); + return prefix + bytes_to_hex(reinterpret_cast(encrypted.data()), encrypted.size()); +} + +std::string KeyAuth::api::unprotect_data(const std::string& data) +{ + const std::string prefix = "keyauth:dpapi:v1:"; + if (data.compare(0, prefix.size(), prefix) != 0 || data.size() <= prefix.size()) + throw request_error("Unsupported protected local data format."); + const auto encoded_size = data.size() - prefix.size(); + if (encoded_size % 2 || encoded_size / 2 > MAXDWORD) + throw request_error("Invalid protected local data length."); + if (sodium_init() < 0) throw request_error("Local data integrity initialization failed."); + std::string encrypted(encoded_size / 2, '\0'); + ScopeWipe wipe_encrypted(encrypted); + size_t decoded = 0; + if (sodium_hex2bin(reinterpret_cast(encrypted.data()), encrypted.size(), + data.data() + prefix.size(), encoded_size, nullptr, &decoded, nullptr) != 0 || decoded != encrypted.size()) + throw request_error("Invalid protected local data encoding."); + std::string plain = transform_saved_data(encrypted, false); + ScopeWipe wipe_plain(plain); + constexpr size_t digest_size = crypto_generichash_BYTES; + if (plain.size() < 8 + digest_size || plain.compare(0, 8, "KA-DATA1") != 0) + throw request_error("Invalid protected local data envelope."); + const auto size = plain.size() - 8 - digest_size; + unsigned char digest[digest_size]{}; + if (crypto_generichash(digest, sizeof(digest), + reinterpret_cast(plain.data() + 8), size, nullptr, 0) != 0) + throw request_error("Local data integrity calculation failed."); + const bool valid = sodium_memcmp(digest, plain.data() + 8 + size, sizeof(digest)) == 0; + SecureZeroMemory(digest, sizeof(digest)); + if (!valid) throw request_error("Protected local data failed its integrity check."); + return plain.substr(8, size); +} + static uint64_t fnv1a64_bytes(const uint8_t* data, size_t len) { uint64_t h = 0xcbf29ce484222325ULL; @@ -339,50 +438,112 @@ void KeyAuth::api::enable_secure_strings(bool enable) } } +std::string KeyAuth::api::initialization_context() const +{ + return keyauth_detail::application_identity(get_ownerid(), get_name(), get_url()); +} + +void KeyAuth::api::bind_initialization_context() +{ + std::string identity = initialization_context(); + ScopeWipe wipe(identity); + initialized_context_ = protect_field(identity, true); +} + +bool KeyAuth::api::initialization_context_valid() const +{ + if (!initialized) return true; + if (initialized_context_.empty()) return false; + try { + std::string expected = protect_field(initialized_context_, false); + ScopeWipe wipe_expected(expected); + std::string actual = initialization_context(); + ScopeWipe wipe_actual(actual); + return expected == actual; + } catch (const std::exception&) { + return false; + } +} + uint64_t KeyAuth::api::compute_auth_seal(uint64_t nonce, long long window) const { const std::string hwid = utils::get_hwid(); std::string material; + ScopeWipe wipe_material(material); material.reserve(256); - material += get_ownerid(); - material += '|'; - material += get_name(); - material += '|'; - material += sessionid; - material += '|'; - material += user_data.username; - material += '|'; - material += hwid; - material += '|'; - material += std::to_string(GetCurrentProcessId()); - material += '|'; - material += std::to_string(reinterpret_cast(this)); - material += '|'; - material += std::to_string(nonce); - material += '|'; - material += std::to_string(window); + const auto append = [&material](const std::string& field) { + material += std::to_string(field.size()); + material += ':'; + material += field; + }; + append(get_ownerid()); + append(get_name()); + append(sessionid); + append(user_data.username); + append(hwid); + append(std::to_string(GetCurrentProcessId())); + append(std::to_string(reinterpret_cast(this))); + append(std::to_string(nonce)); + append(std::to_string(window)); + append(enckey); + append(initialized_context_); + struct ScopedKey final { + std::array value; + ~ScopedKey() { SecureZeroMemory(value.data(), value.size()); } + } key{auth_seal_key_}; + if (!CryptUnprotectMemory(key.value.data(), static_cast(key.value.size()), + CRYPTPROTECTMEMORY_SAME_PROCESS)) + throw request_error("Windows authentication key protection failed."); uint64_t seal = 0; const int status = crypto_shorthash(reinterpret_cast(&seal), - reinterpret_cast(material.data()), material.size(), auth_seal_key_.data()); + reinterpret_cast(material.data()), material.size(), key.value.data()); secure_zero(material); if (status != 0) throw request_error("Authentication state verification failed."); return seal; } +void KeyAuth::api::store_auth_entitlements(bool reset_profile) +{ + nlohmann::json state; + if (reset_profile) { + state["subscriptions"] = nlohmann::json::array(); + for (const auto& sub : user_data.subscriptions) + state["subscriptions"].push_back({{"name", sub.name}, {"expiry", sub.expiry}}); + } else { + std::string plain = protect_field(enckey, false); + ScopeWipe wipe_plain(plain); + state = nlohmann::json::parse(plain); + } + state["paid"] = response.isPaid; + std::string plain = state.dump(); + ScopeWipe wipe_plain(plain); + std::string protected_state = protect_field(plain, true); + secure_zero(enckey); + enckey = std::move(protected_state); +} + bool KeyAuth::api::has_active_subscription() const { - const auto now = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - for (const auto& sub : user_data.subscriptions) { - if (sub.name.empty() || sub.expiry.empty()) - continue; - try { - if (std::stoll(sub.expiry) > now) { - return true; + try { + std::string plain = protect_field(enckey, false); + ScopeWipe wipe_plain(plain); + const auto state = nlohmann::json::parse(plain); + if (state.value("paid", false)) return true; + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + for (const auto& sub : state.at("subscriptions")) { + const auto name = sub.at("name").get(); + const auto expiry = sub.at("expiry").get(); + if (name.empty() || expiry.empty()) continue; + try { + size_t consumed = 0; + const auto expiration = std::stoll(expiry, &consumed); + if (consumed == expiry.size() && expiration > now) return true; + } catch (const std::invalid_argument&) { + } catch (const std::out_of_range&) { } } - catch (...) { - } + } catch (const std::exception&) { } return false; } @@ -391,6 +552,7 @@ void KeyAuth::api::reset_auth_runtime() { LoggedIn.store(false); auth_nonce_.store(0); + secure_zero(enckey); SecureZeroMemory(auth_seal_key_.data(), auth_seal_key_.size()); auth_window_.store(0); auth_seal_.store(0); @@ -400,25 +562,33 @@ void KeyAuth::api::reset_auth_runtime() void KeyAuth::api::mark_authenticated() { - uint64_t nonce = 0; - do { - if (BCryptGenRandom(nullptr, reinterpret_cast(&nonce), sizeof(nonce), + try { + uint64_t nonce = 0; + do { + if (BCryptGenRandom(nullptr, reinterpret_cast(&nonce), sizeof(nonce), + BCRYPT_USE_SYSTEM_PREFERRED_RNG) < 0) { + reset_auth_runtime(); + throw request_error("Windows cryptographic random generation failed."); + } + } while (nonce == 0); + if (BCryptGenRandom(nullptr, auth_seal_key_.data(), static_cast(auth_seal_key_.size()), BCRYPT_USE_SYSTEM_PREFERRED_RNG) < 0) { reset_auth_runtime(); throw request_error("Windows cryptographic random generation failed."); } - } while (nonce == 0); - if (BCryptGenRandom(nullptr, auth_seal_key_.data(), static_cast(auth_seal_key_.size()), - BCRYPT_USE_SYSTEM_PREFERRED_RNG) < 0) { + if (!CryptProtectMemory(auth_seal_key_.data(), static_cast(auth_seal_key_.size()), + CRYPTPROTECTMEMORY_SAME_PROCESS)) + throw request_error("Windows authentication key protection failed."); + store_auth_entitlements(true); + const auto window = keyauth_detail::auth_window_now(); + auth_nonce_.store(nonce); + auth_window_.store(window); + auth_seal_.store(compute_auth_seal(nonce, window)); + LoggedIn.store(true); + } catch (...) { reset_auth_runtime(); - throw request_error("Windows cryptographic random generation failed."); + throw; } - const auto window = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count() / 90; - auth_nonce_.store(nonce); - auth_window_.store(window); - auth_seal_.store(compute_auth_seal(nonce, window)); - LoggedIn.store(true); } void KeyAuth::api::refresh_auth_runtime() @@ -428,8 +598,8 @@ void KeyAuth::api::refresh_auth_runtime() const uint64_t nonce = auth_nonce_.load(); if (nonce == 0) return; - const auto window = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count() / 90; + const auto window = keyauth_detail::auth_window_now(); + store_auth_entitlements(false); auth_window_.store(window); auth_seal_.store(compute_auth_seal(nonce, window)); } @@ -442,7 +612,7 @@ bool KeyAuth::api::authenticated(bool require_paid) const bool KeyAuth::api::local_auth_valid(bool require_paid, bool require_fresh) const { - if (!LoggedIn.load()) + if (!LoggedIn.load() || !initialization_context_valid()) return false; if (sessionid.empty() || user_data.username.empty()) return false; @@ -453,15 +623,19 @@ bool KeyAuth::api::local_auth_valid(bool require_paid, bool require_fresh) const if (nonce == 0 || sealed_window == 0 || sealed == 0) return false; - const uint64_t expected = compute_auth_seal(nonce, sealed_window); + uint64_t expected = 0; + try { + expected = compute_auth_seal(nonce, sealed_window); + } catch (const request_error&) { + return false; + } if (sodium_memcmp(&expected, &sealed, sizeof(sealed)) != 0) return false; - const auto current_window = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count() / 90; + const auto current_window = keyauth_detail::auth_window_now(); if (require_fresh && !keyauth_detail::auth_window_fresh(sealed_window, current_window)) return false; - if (require_paid && !has_active_subscription() && !response.isPaid) + if (require_paid && !has_active_subscription()) return false; return true; @@ -489,7 +663,7 @@ static std::string decode_pubkey_hex(const uint8_t* obf, size_t len, uint8_t key static std::string to_lower_ascii(std::string v) { std::transform(v.begin(), v.end(), v.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return keyauth_detail::ascii_lower(c); }); return v; } @@ -623,16 +797,14 @@ static bool module_contains_ascii(const std::string& needle) return false; } -struct ScopeWipe final { - std::string* value; - explicit ScopeWipe(std::string& v) noexcept : value(&v) {} - ~ScopeWipe() noexcept { securewipe(*value); } -}; - void KeyAuth::api::init() { std::lock_guard api_lock(api_mutex_); - if (initialized) return; + if (initialized) { + if (!initialization_context_valid()) throw request_error("Application configuration changed after initialization. Create a new API instance."); + return; + } + secure_zero(initialized_context_); static const CURLcode curl_init_result = curl_global_init(CURL_GLOBAL_DEFAULT); if (curl_init_result != CURLE_OK) throw request_error("CURL global initialization failed."); (void)get_public_key_hex(); @@ -649,6 +821,7 @@ void KeyAuth::api::init() XorStr("&hash=") + curl_escape(nullptr, hash) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); // to ensure people removed secret from main.cpp (some people will forget to) const std::string resolved_path = get_path(); if (resolved_path.find("https") != std::string::npos) { @@ -656,25 +829,33 @@ void KeyAuth::api::init() KA_EXIT(0); } - if (resolved_path != "" || !resolved_path.empty()) { + if (!resolved_path.empty()) { - if (!std::filesystem::exists(resolved_path)) { + const auto token_path = keyauth_detail::native_file_path(resolved_path); + if (!std::filesystem::exists(token_path)) { MessageBoxA(0, XorStr("File not found. Please make sure the file exists.").c_str(), NULL, MB_ICONERROR); KA_EXIT(0); } //get the contents of the file - std::ifstream file(resolved_path); + std::ifstream file(token_path); + if (!file) { + throw request_error("Cannot open API token file."); + } std::string token; std::string thash; - std::getline(file, token); + ScopeWipe wipe_token(token); + if (!std::getline(file, token)) { + throw request_error("Cannot read API token file."); + } - thash = keyauth_detail::file_md5(std::filesystem::path(resolved_path)); + thash = keyauth_detail::file_md5(token_path); data += XorStr("&token=").c_str() + curl_escape(nullptr, token); data += XorStr("&thash=").c_str() + curl_escape(nullptr, thash); } auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); if (response == XorStr("KeyAuth_Invalid").c_str()) { MessageBoxA(0, XorStr("Application not found. Please copy strings directly from dashboard.").c_str(), NULL, MB_ICONERROR); @@ -708,9 +889,9 @@ void KeyAuth::api::init() Sleep(100); } sessionid = json[(XorStr("sessionid"))]; - authenticated_host_ = keyauth_detail::parse_api_endpoint(get_url()).host; - initialized = true; + bind_initialization_context(); load_app_data(json[(XorStr("appinfo"))]); + initialized = true; } else if (json[(XorStr("message"))] == XorStr("invalidver")) { @@ -773,7 +954,9 @@ void KeyAuth::api::login(std::string username, std::string password, std::string XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); //std::cout << "[DEBUG] Login response: " << response << std::endl; std::hash hasher; int expectedHash = hasher(42); @@ -820,7 +1003,8 @@ void KeyAuth::api::login(std::string username, std::string password, std::string nlohmann::json KeyAuth::api::email_reply(std::string data) { ScopeWipe wipe(data); - const auto raw = req(data, get_url()); + auto raw = req(data, get_url()); + ScopeWipe wipe_raw(raw); auto reply = nlohmann::json::parse(raw, nullptr, false); try { keyauth_detail::validate_api_reply(reply, get_ownerid()); @@ -960,8 +1144,10 @@ void KeyAuth::api::chatget(std::string channel) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); load_channel_data(json); } @@ -980,8 +1166,10 @@ bool KeyAuth::api::chatsend(std::string message, std::string channel) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); load_response_data(json); return json[XorStr("success")]; @@ -999,8 +1187,10 @@ void KeyAuth::api::changeUsername(std::string newusername) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); int result = VerifyPayload(signature, signatureTimestamp, response); @@ -1043,8 +1233,10 @@ KeyAuth::api::Tfa& KeyAuth::api::enable2fa(std::string code) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); if (json.contains("2fa")) { @@ -1078,8 +1270,10 @@ KeyAuth::api::Tfa& KeyAuth::api::disable2fa(std::string code) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); @@ -1138,9 +1332,11 @@ void KeyAuth::api::web_login() // from https://perpetualprogrammers.wordpress.com/2016/05/22/the-http-server-api/ // Initialize the API. + LocalHttpResources listener; ULONG result = 0; HTTPAPI_VERSION version = HTTPAPI_VERSION_2; result = HttpInitialize(version, HTTP_INITIALIZE_SERVER, 0); + listener.initialized = result == NO_ERROR; if (result == ERROR_INVALID_PARAMETER) { MessageBoxA(NULL, "The Flags parameter contains an unsupported value.", "Error", MB_ICONEXCLAMATION); @@ -1152,7 +1348,7 @@ void KeyAuth::api::web_login() } // Create server session. - HTTP_SERVER_SESSION_ID serverSessionId; + auto& serverSessionId = listener.session; result = HttpCreateServerSession(version, &serverSessionId, 0); if (result == ERROR_REVISION_MISMATCH) { @@ -1171,7 +1367,7 @@ void KeyAuth::api::web_login() } // Create URL group. - HTTP_URL_GROUP_ID groupId; + auto& groupId = listener.group; result = HttpCreateUrlGroup(serverSessionId, &groupId, 0); if (result == ERROR_INVALID_PARAMETER) { @@ -1185,7 +1381,7 @@ void KeyAuth::api::web_login() } // Create request queue. - HANDLE requestQueueHandle; + auto& requestQueueHandle = listener.queue; result = HttpCreateRequestQueue(version, NULL, NULL, 0, &requestQueueHandle); if (result == ERROR_REVISION_MISMATCH) { @@ -1219,7 +1415,7 @@ void KeyAuth::api::web_login() } // Attach request queue to URL group. - HTTP_BINDING_INFO info; + HTTP_BINDING_INFO info{}; info.Flags.Present = 1; info.RequestQueueHandle = requestQueueHandle; result = HttpSetUrlGroupProperty(groupId, HttpServerBindingProperty, &info, sizeof(info)); @@ -1273,12 +1469,12 @@ void KeyAuth::api::web_login() // Wait for a request. HTTP_REQUEST_ID requestId = 0; HTTP_SET_NULL_ID(&requestId); - int bufferSize = 4096; + int bufferSize = 64 * 1024; int requestSize = sizeof(HTTP_REQUEST) + bufferSize; auto buffer = std::make_unique(requestSize); PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer.get(); RtlZeroMemory(buffer.get(), requestSize); - ULONG bytesReturned; + ULONG bytesReturned = 0; result = HttpReceiveHttpRequest( requestQueueHandle, requestId, @@ -1288,16 +1484,33 @@ void KeyAuth::api::web_login() &bytesReturned, NULL ); + if (result == ERROR_MORE_DATA) { + if (pRequest->RequestId) HttpCancelHttpRequest(requestQueueHandle, pRequest->RequestId, nullptr); + continue; + } + if (result == ERROR_CONNECTION_INVALID || result == ERROR_HANDLE_EOF) continue; + if (result != NO_ERROR) + throw request_error("Local HTTP request reception failed (code " + std::to_string(result) + ")."); // Display some information about the request. // wprintf(L"Full URL: %ws\n", pRequest->CookedUrl.pFullUrl); // wprintf(L" Path: %ws\n", pRequest->CookedUrl.pAbsPath); // wprintf(L" Query: %ws\n", pRequest->CookedUrl.pQueryString); - std::wstring ws(pRequest->CookedUrl.pQueryString); + std::wstring ws; + try { + ws = keyauth_detail::bounded_http_query(pRequest->CookedUrl.pQueryString, + pRequest->CookedUrl.QueryStringLength, buffer.get(), requestSize); + } catch (const std::invalid_argument&) { + HttpCancelHttpRequest(requestQueueHandle, pRequest->RequestId, nullptr); + continue; + } std::string myVarS = std::string(ws.begin(), ws.end()); + ScopeWipe wipe_query(myVarS); std::string user = get_str_between_two_str(myVarS, "?user=", "&"); std::string token = get_str_between_two_str(myVarS, "&token=", ""); + ScopeWipe wipe_token(token); + ScopeWipe wipe_user(user); // std::cout << get_str_between_two_str(CW2A(pRequest->CookedUrl.pQueryString), "?", "&") << std::endl; @@ -1309,7 +1522,7 @@ void KeyAuth::api::web_login() RtlZeroMemory(&response, sizeof(response)); response.StatusCode = 200; - response.pReason = static_cast(XorStr("OK").c_str()); + response.pReason = "OK"; response.ReasonLength = (USHORT)strlen(response.pReason); // https://social.msdn.microsoft.com/Forums/vstudio/en-US/6d468747-2221-4f4a-9156-f98f355a9c08/using-httph-to-set-up-an-https-server-that-is-queried-by-a-client-that-uses-cross-origin-requests?forum=vcgeneral @@ -1343,6 +1556,11 @@ void KeyAuth::api::web_login() continue; } + if (user.empty() || token.empty()) { + HttpCancelHttpRequest(requestQueueHandle, pRequest->RequestId, nullptr); + continue; + } + // keyauth request std::string hwid = utils::get_hwid(); auto data = @@ -1353,7 +1571,9 @@ void KeyAuth::api::web_login() XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto resp = req(data, get_url()); + ScopeWipe wipe_response_body(resp); std::hash hasher; int expectedHash = hasher(42); @@ -1385,19 +1605,21 @@ void KeyAuth::api::web_login() HTTP_RESPONSE response; RtlZeroMemory(&response, sizeof(response)); + std::string response_reason; bool success = true; if (json[(XorStr("success"))]) { load_user_data(json[(XorStr("info"))]); response.StatusCode = 420; - response.pReason = XorStr("SHEESH").c_str(); + response.pReason = "SHEESH"; response.ReasonLength = (USHORT)strlen(response.pReason); } else { response.StatusCode = 200; - response.pReason = static_cast(json[(XorStr("message"))]).c_str(); + response_reason = keyauth_detail::http_reason(json.at("message").get()); + response.pReason = response_reason.c_str(); response.ReasonLength = (USHORT)strlen(response.pReason); success = false; } @@ -1457,27 +1679,34 @@ void KeyAuth::api::button(std::string button) // from https://perpetualprogrammers.wordpress.com/2016/05/22/the-http-server-api/ // Initialize the API. + LocalHttpResources listener; ULONG result = 0; HTTPAPI_VERSION version = HTTPAPI_VERSION_2; result = HttpInitialize(version, HTTP_INITIALIZE_SERVER, 0); + listener.initialized = result == NO_ERROR; + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Create server session. - HTTP_SERVER_SESSION_ID serverSessionId; + auto& serverSessionId = listener.session; result = HttpCreateServerSession(version, &serverSessionId, 0); + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Create URL group. - HTTP_URL_GROUP_ID groupId; + auto& groupId = listener.group; result = HttpCreateUrlGroup(serverSessionId, &groupId, 0); + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Create request queue. - HANDLE requestQueueHandle; + auto& requestQueueHandle = listener.queue; result = HttpCreateRequestQueue(version, NULL, NULL, 0, &requestQueueHandle); + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Attach request queue to URL group. - HTTP_BINDING_INFO info; + HTTP_BINDING_INFO info{}; info.Flags.Present = 1; info.RequestQueueHandle = requestQueueHandle; result = HttpSetUrlGroupProperty(groupId, HttpServerBindingProperty, &info, sizeof(info)); + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Add URLs to URL group. std::wstring output; @@ -1485,6 +1714,7 @@ void KeyAuth::api::button(std::string button) output = std::wstring(L"http://localhost:1337/") + output; PCWSTR url = output.c_str(); result = HttpAddUrlToUrlGroup(groupId, url, 0, 0); + if (result != NO_ERROR) throw request_error("Local HTTP listener setup failed (code " + std::to_string(result) + ")."); // Announce that it is running. // wprintf(L"Listening. Please submit requests to: %s\n", url); @@ -1496,12 +1726,12 @@ void KeyAuth::api::button(std::string button) // Wait for a request. HTTP_REQUEST_ID requestId = 0; HTTP_SET_NULL_ID(&requestId); - int bufferSize = 4096; + int bufferSize = 64 * 1024; int requestSize = sizeof(HTTP_REQUEST) + bufferSize; auto buffer = std::make_unique(requestSize); PHTTP_REQUEST pRequest = (PHTTP_REQUEST)buffer.get(); RtlZeroMemory(buffer.get(), requestSize); - ULONG bytesReturned; + ULONG bytesReturned = 0; result = HttpReceiveHttpRequest( requestQueueHandle, requestId, @@ -1511,6 +1741,13 @@ void KeyAuth::api::button(std::string button) &bytesReturned, NULL ); + if (result == ERROR_MORE_DATA) { + if (pRequest->RequestId) HttpCancelHttpRequest(requestQueueHandle, pRequest->RequestId, nullptr); + continue; + } + if (result == ERROR_CONNECTION_INVALID || result == ERROR_HANDLE_EOF) continue; + if (result != NO_ERROR) + throw request_error("Local HTTP request reception failed (code " + std::to_string(result) + ")."); going = false; @@ -1532,7 +1769,7 @@ void KeyAuth::api::button(std::string button) HTTP_RESPONSE response; RtlZeroMemory(&response, sizeof(response)); response.StatusCode = 420; - response.pReason = XorStr("SHEESH").c_str(); + response.pReason = "SHEESH"; response.ReasonLength = (USHORT)strlen(response.pReason); // https://social.msdn.microsoft.com/Forums/vstudio/en-US/6d468747-2221-4f4a-9156-f98f355a9c08/using-httph-to-set-up-an-https-server-that-is-queried-by-a-client-that-uses-cross-origin-requests?forum=vcgeneral @@ -1585,7 +1822,9 @@ void KeyAuth::api::regstr(std::string username, std::string password, std::strin XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1641,7 +1880,9 @@ void KeyAuth::api::upgrade(std::string username, std::string key) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1702,7 +1943,9 @@ void KeyAuth::api::license(std::string key, std::string code) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1756,7 +1999,9 @@ void KeyAuth::api::setvar(std::string var, std::string vardata) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); load_response_data(json); } @@ -1772,7 +2017,9 @@ std::string KeyAuth::api::getvar(std::string var) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1814,7 +2061,9 @@ void KeyAuth::api::ban(std::string reason) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1856,7 +2105,9 @@ bool KeyAuth::api::checkblack() { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1888,7 +2139,7 @@ void KeyAuth::api::check(bool check_paid) { std::lock_guard api_lock(api_mutex_); checkInit(); - if (LoggedIn.load() && !local_auth_valid(check_paid, false)) { + if (LoggedIn.load() && !local_auth_valid(false, false)) { reset_auth_runtime(); return; } @@ -1898,13 +2149,16 @@ void KeyAuth::api::check(bool check_paid) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); std::string endpoint = get_url(); if (check_paid) { - endpoint += "?check_paid=1"; + endpoint += (endpoint.find('?') == std::string::npos ? "?" : "&"); + endpoint += "check_paid=1"; } auto response = req(data, endpoint); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1951,7 +2205,9 @@ std::string KeyAuth::api::var(std::string varid) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -1987,10 +2243,13 @@ void KeyAuth::api::log(std::string message) { checkInit(); ScopeWipe wipe_message(message); - char acUserName[100]; - DWORD nUserName = sizeof(acUserName); - GetUserNameA(acUserName, &nUserName); - std::string UsernamePC = acUserName; + wchar_t acUserName[UNLEN + 1]{}; + DWORD nUserName = static_cast(std::size(acUserName)); + std::string UsernamePC; + if (GetUserNameW(acUserName, &nUserName) && nUserName > 0 && nUserName <= std::size(acUserName)) + UsernamePC = wide_to_utf8(acUserName); + SecureZeroMemory(acUserName, sizeof(acUserName)); + ScopeWipe wipe_pcuser(UsernamePC); auto data = XorStr("type=log") + @@ -1999,6 +2258,7 @@ void KeyAuth::api::log(std::string message) { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); req(data, get_url()); } @@ -2008,18 +2268,16 @@ std::vector KeyAuth::api::download(std::string fileid) { checkInit(); ScopeWipe wipe_fileid(fileid); - auto to_uc_vector = [](std::string value) { - return std::vector(value.data(), value.data() + value.length() ); - }; - auto data = XorStr("type=file") + XorStr("&fileid=") + curl_escape(nullptr, fileid) + XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto reply = req(data, get_url()); + ScopeWipe wipe_reply(reply); auto json = nlohmann::json::parse(reply, nullptr, false); if (json.is_discarded() || !json.is_object()) { response.success = false; @@ -2031,12 +2289,13 @@ std::vector KeyAuth::api::download(std::string fileid) { response.success = false; throw request_error("Download response has no contents field."); } - const auto contents = json["contents"].get(); + auto& contents = json["contents"].get_ref(); + ScopeWipe wipe_contents(contents); if (contents.size() % 2 || (!contents.empty() && !is_hex_string(contents))) { response.success = false; throw request_error("Download contents are not valid hexadecimal data."); } - return to_uc_vector(hexDecode(contents)); + return keyauth_detail::decode_download(contents); } @@ -2058,7 +2317,9 @@ std::string KeyAuth::api::webhook(std::string id, std::string params, std::strin XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -2100,8 +2361,10 @@ std::string KeyAuth::api::fetchonline() XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); @@ -2149,8 +2412,10 @@ void KeyAuth::api::fetchstats() XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); std::hash hasher; int expectedHash = hasher(42); int result = VerifyPayload(signature, signatureTimestamp, response); @@ -2198,7 +2463,9 @@ void KeyAuth::api::forgot(std::string username, std::string email) XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); load_response_data(json); } @@ -2212,7 +2479,9 @@ void KeyAuth::api::logout() { XorStr("&sessionid=") + curl_escape(nullptr, sessionid) + XorStr("&name=") + curl_escape(nullptr, get_name()) + XorStr("&ownerid=") + curl_escape(nullptr, get_ownerid()); + ScopeWipe wipe_request_data(data); auto response = req(data, get_url()); + ScopeWipe wipe_response_body(response); auto json = response_decoder.parse(response); if (json[(XorStr("success"))]) { @@ -2230,6 +2499,8 @@ void KeyAuth::api::logout() { //clear enckey secure_zero(enckey); reset_auth_runtime(); + initialized = false; + secure_zero(initialized_context_); } load_response_data(json); @@ -2380,13 +2651,13 @@ void KeyAuth::api::reset_lockout(lockout_state& state) state.locked_until = std::chrono::steady_clock::time_point{}; } -int VerifyPayload(std::string signature, std::string timestamp, std::string body) +int VerifyPayload(const std::string& signature, const std::string& timestamp, const std::string& body, long long received_at) { integrity_check(); const auto now = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count(); const auto result = keyauth_detail::verify_payload(signature, timestamp, body, - get_public_key_hex(), now); + get_public_key_hex(), received_at > 0 ? received_at : now); if (result == keyauth_detail::PayloadResult::timestamp_skew) { throw KeyAuth::request_error("Signed response timestamp is outside the 120 second window. Synchronize the system clock and retry."); } @@ -2397,19 +2668,6 @@ int VerifyPayload(std::string signature, std::string timestamp, std::string body return (42 ^ 0xA5A5) & 0xFFFF; } -// credits https://stackoverflow.com/a/3790661 -static std::string hexDecode(const std::string& hex) -{ - int len = hex.length(); - std::string newString; - for (int i = 0; i < len; i += 2) - { - std::string byte = hex.substr(i, 2); - char chr = (char)(int)strtol(byte.c_str(), NULL, 16); - newString.push_back(chr); - } - return newString; -} // credits https://stackoverflow.com/a/43002794 std::string get_str_between_two_str(const std::string& s, const std::string& start_delim, @@ -2434,7 +2692,13 @@ std::string curl_escape(CURL* curl, const std::string& input) if (input.size() > INT_MAX) throw KeyAuth::request_error("API field is too large."); char* encoded = curl_easy_escape(curl, input.data(), static_cast(input.size())); if (!encoded) throw KeyAuth::request_error("Could not encode API field."); - std::unique_ptr owner(encoded, curl_free); + const auto release_encoded = [](char* value) noexcept { + if (value) { + SecureZeroMemory(value, std::strlen(value)); + curl_free(value); + } + }; + std::unique_ptr owner(encoded, release_encoded); return std::string(encoded); } @@ -2488,7 +2752,7 @@ static bool is_https_url(const std::string& url) if (url.size() < prefix.size()) return false; for (size_t i = 0; i < prefix.size(); ++i) { - const char c = static_cast(std::tolower(static_cast(url[i]))); + const char c = keyauth_detail::ascii_lower(static_cast(url[i])); if (c != prefix[i]) return false; } @@ -3118,6 +3382,7 @@ KeyAuth::api::~api() secure_zero(sessionid); secure_zero(enckey); secure_zero(magic_link_email_); + secure_zero(initialized_context_); secure_zero(name_enc_); secure_zero(ownerid_enc_); secure_zero(version_enc_); @@ -3163,8 +3428,16 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { integrity_check(); // usage: keep this in req() so every api call is protected -nigel if (LoggedIn.load() && !request_bypasses_local_auth(data) && !local_auth_valid(false)) { - reset_auth_runtime(); - throw request_error(XorStr("local auth gate failed.")); + if (!local_auth_valid(false, false)) { + reset_auth_runtime(); + throw request_error(XorStr("local auth gate failed.")); + } + check(); + if (!response.success || !local_auth_valid(false)) + throw request_error("Session validation failed before the requested operation."); + response.success = false; + signature.clear(); + signatureTimestamp.clear(); } keyauth_detail::ApiEndpoint endpoint; try { @@ -3173,8 +3446,16 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { throw request_error(e.what()); } const std::string host = endpoint.host; - if (!authenticated_host_.empty() && host != authenticated_host_) - throw request_error("API hostname changed after initialization."); + const std::string operation = data.substr(0, data.find('&')).substr(5); + if (!initialization_context_valid()) + throw request_error("Application configuration changed after initialization. Create a new API instance."); + const auto configured = keyauth_detail::parse_api_endpoint(get_url()); + if (!keyauth_detail::request_endpoint_matches(configured.url, endpoint.url, operation)) + throw request_error("Request endpoint does not match the configured application."); + const std::string requested_owner = get_ownerid(); + const std::string requested_name = get_name(); + std::string requested_session = sessionid; + ScopeWipe wipe_requested_session(requested_session); if (hosts_override_present(host)) throw request_error("Windows hosts file overrides the configured API hostname."); if (!allowed_hosts.empty() && std::none_of(allowed_hosts.begin(), allowed_hosts.end(), @@ -3187,11 +3468,11 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { } std::unique_ptr curl_guard(curl, curl_easy_cleanup); - const std::string operation = data.substr(0, data.find('&')).substr(5); const bool is_file_request = operation == "file"; const bool query_request = is_file_request || keyauth_detail::email_request_uses_get(operation); keyauth_detail::ResponseBuffer response_buffer{{}, is_file_request ? max_download_response_bytes : max_api_response_bytes}; auto& to_return = response_buffer.body; + ScopeWipe wipe_response_buffer(to_return); SignatureHeaders sig_headers; std::string request_url = endpoint.url; ScopeWipe url_wipe(request_url); @@ -3219,8 +3500,10 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { throw request_error("Could not apply the required API transport policy."); if (curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS) != CURLE_OK) throw request_error("Could not apply the required API transport policy."); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); + if (curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60L) != CURLE_OK || + curl_easy_setopt(curl, CURLOPT_TIMEOUT, is_file_request ? 0L : 120L) != CURLE_OK || + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L) != CURLE_OK) + throw request_error("Could not apply the required API connection settings."); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); #if LIBCURL_VERSION_NUM >= 0x072200 @@ -3330,7 +3613,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (sig_ok) { int verify_result; try { - verify_result = VerifyPayload(sig_hex, ts, to_return); + verify_result = VerifyPayload(sig_hex, ts, to_return, is_file_request ? sig_headers.timestamp_received_at : 0); } catch (...) { curl_guard.reset(); throw; @@ -3358,7 +3641,8 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { curl_guard.reset(); throw request_error(XorStr("public key decode failed in request guard.")); } - const std::string signed_message_guard = ts + to_return; + std::string signed_message_guard = ts + to_return; + ScopeWipe wipe_signed_message(signed_message_guard); if (crypto_sign_ed25519_verify_detached(sig_guard, reinterpret_cast(signed_message_guard.data()), signed_message_guard.size(), @@ -3370,9 +3654,11 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { if (sig_ok) { const auto envelope = nlohmann::json::parse(to_return, nullptr, false); - if (envelope.is_object() && envelope.contains("ownerid") && - (!envelope["ownerid"].is_string() || envelope["ownerid"] != get_ownerid())) - throw request_error("API response owner does not match the configured application."); + try { + keyauth_detail::validate_response_context(envelope, requested_owner, requested_name, requested_session, operation); + } catch (const std::exception& e) { + throw request_error(e.what()); + } if (http_status >= 400 && (!envelope.is_object() || !envelope.contains("success") || !envelope["success"].is_boolean() || envelope["success"].get())) throw request_error("Invalid signed API error response."); @@ -3388,9 +3674,9 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { std::string host_lower = host; std::string eff_lower = eff_host; std::transform(host_lower.begin(), host_lower.end(), host_lower.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return keyauth_detail::ascii_lower(c); }); std::transform(eff_lower.begin(), eff_lower.end(), eff_lower.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return keyauth_detail::ascii_lower(c); }); if (!eff_lower.empty() && eff_lower != host_lower) { curl_guard.reset(); throw request_error(XorStr("effective url host mismatch.")); @@ -3405,7 +3691,7 @@ std::string KeyAuth::api::req(std::string data, const std::string& url) { secure_zero(data); signature = sig_hex; signatureTimestamp = ts; - return to_return; + return std::move(to_return); } [[noreturn]] void error(std::string message) { @@ -3421,15 +3707,11 @@ std::string checksum() } std::string getPath() { - const char* programDataPath = std::getenv("ALLUSERSPROFILE"); - - if (programDataPath != nullptr) { - return std::string(programDataPath); - } - else { - - return std::filesystem::current_path().string(); + const wchar_t* program_data = _wgetenv(L"ALLUSERSPROFILE"); + if (program_data && *program_data) { + return wide_to_utf8(program_data); } + return std::filesystem::current_path().u8string(); } void RedactField(nlohmann::json& jsonObject, const std::string& fieldName) @@ -3500,36 +3782,18 @@ void KeyAuth::api::debugInfo(std::string data, std::string url, std::string resp redacted_data = datas.dump(); } - //gets the path - std::string path = getPath(); - - //fetch filename - - TCHAR filename[MAX_PATH]; - GetModuleFileName(NULL, filename, MAX_PATH); - - TCHAR* filename_only = PathFindFileName(filename); - - std::wstring filenameOnlyString(filename_only); - - std::string filenameOnly(filenameOnlyString.begin(), filenameOnlyString.end()); - - /////////////////////// - - //creates variables for the paths needed :smile: - std::string KeyAuthPath = path + "\\KeyAuth"; - std::string logPath = KeyAuthPath + "\\Debug\\" + filenameOnly.substr(0, filenameOnly.size() - 4); - - //basically loops until we have all the paths - if (!std::filesystem::exists(KeyAuthPath) || !std::filesystem::exists(KeyAuthPath + "\\Debug") || !std::filesystem::exists(logPath)) { - - if (!std::filesystem::exists(KeyAuthPath)) { std::filesystem::create_directory(KeyAuthPath); } - - if (!std::filesystem::exists(KeyAuthPath + "\\Debug")) { std::filesystem::create_directory(KeyAuthPath + "\\Debug"); } - - if (!std::filesystem::exists(logPath)) { std::filesystem::create_directory(logPath); } - + // Keep debug paths in UTF-16; narrowing Unicode filenames loses characters. -nigel + std::wstring executable_path(32768, L'\0'); + const DWORD path_length = GetModuleFileNameW(nullptr, executable_path.data(), + static_cast(executable_path.size())); + if (!path_length || path_length >= executable_path.size()) { + throw request_error("Cannot locate executable for debug log."); } + executable_path.resize(path_length); + + const auto executable_name = std::filesystem::path(executable_path).filename().stem(); + const auto log_path = keyauth_detail::native_file_path(getPath()) / L"KeyAuth" / L"Debug" / executable_name; + std::filesystem::create_directories(log_path); if (response.length() >= 500) { return; } @@ -3541,7 +3805,7 @@ void KeyAuth::api::debugInfo(std::string data, std::string url, std::string resp std::strftime(time, sizeof(time), "%m-%d-%Y", localTime); - std::ofstream logfile(logPath + "\\" + time + ".txt", std::ios::app); + std::ofstream logfile(log_path / (std::string(time) + ".txt"), std::ios::app); //get time int hours = localTime->tm_hour; @@ -3634,6 +3898,8 @@ void KeyAuth::api::checkInit() { if (!initialized) { error(XorStr("You need to run KeyAuthApp.init() before other API functions.")); } + if (!initialization_context_valid()) + throw request_error("Application configuration changed after initialization. Create a new API instance."); } void integrity_check() { diff --git a/auth.hpp b/auth.hpp index 932288d..d044f7f 100644 --- a/auth.hpp +++ b/auth.hpp @@ -26,10 +26,38 @@ #include namespace keyauth_detail { +// HTTP names use ASCII casing regardless of the application's locale. -nigel +inline char ascii_lower(unsigned char value) noexcept { + if (value >= 'A' && value <= 'Z') { + return static_cast(value + ('a' - 'A')); + } + return static_cast(value); +} +inline std::wstring bounded_http_query(const wchar_t* query, size_t bytes, + const void* buffer, size_t buffer_size) { + if (!bytes) return {}; + const auto start = reinterpret_cast(buffer); + const auto address = reinterpret_cast(query); + if (!buffer || !query || bytes % sizeof(wchar_t) || address % alignof(wchar_t) || + address < start || address - start > buffer_size || bytes > buffer_size - (address - start)) + throw std::invalid_argument("Invalid local HTTP query buffer."); + std::wstring value(query, bytes / sizeof(wchar_t)); + if (value.find(L'\0') != std::wstring::npos) + throw std::invalid_argument("Invalid local HTTP query string."); + return value; +} +inline std::string http_reason(std::string value) { + if (value.size() > USHRT_MAX) value.resize(USHRT_MAX); + for (auto& c : value) if (static_cast(c) < 32 || c == 127) c = ' '; + return value; +} struct SignatureHeaders { std::string signature; std::string timestamp; bool conflicting = false; + bool signature_seen = false; + bool timestamp_seen = false; + long long timestamp_received_at = 0; }; // libcurl may deliver CONNECT, informational, final and trailer header blocks. @@ -43,16 +71,21 @@ inline bool consume_header(SignatureHeaders& out, std::string line) { if (colon == std::string::npos) return true; std::string key = line.substr(0, colon); std::transform(key.begin(), key.end(), key.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return keyauth_detail::ascii_lower(c); }); std::string* target = nullptr; if (key == "x-signature-ed25519") target = &out.signature; if (key == "x-signature-timestamp") target = &out.timestamp; if (!target) return true; + bool& seen = target == &out.signature ? out.signature_seen : out.timestamp_seen; std::string value = line.substr(colon + 1); const auto first = value.find_first_not_of(" \t\r\n"); const auto last = value.find_last_not_of(" \t\r\n"); value = first == std::string::npos ? "" : value.substr(first, last - first + 1); - if (!target->empty() && *target != value) out.conflicting = true; + if (!seen && target == &out.timestamp) + out.timestamp_received_at = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + if (seen && *target != value) out.conflicting = true; + seen = true; *target = value; return true; } @@ -64,6 +97,13 @@ inline bool consume_header(SignatureHeaders& out, std::string line) { #include namespace keyauth_detail { +inline long long auth_window_for_seconds(long long seconds) { + return seconds < 0 ? 0 : seconds / 90 + 1; +} +inline long long auth_window_now() { + return auth_window_for_seconds(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); +} inline bool auth_window_fresh(long long sealed, long long current) { return sealed > 0 && current >= sealed && current - sealed <= 2; } @@ -91,10 +131,12 @@ inline PayloadResult verify_payload(const std::string& signature, sodium_hex2bin(pk, sizeof(pk), public_key.data(), public_key.size(), nullptr, &size, nullptr) != 0 || size != sizeof(pk)) return PayloadResult::public_key_format; - const std::string message = timestamp + body; - return crypto_sign_verify_detached(sig, + std::string message = timestamp + body; + const auto result = crypto_sign_verify_detached(sig, reinterpret_cast(message.data()), message.size(), pk) == 0 ? PayloadResult::valid : PayloadResult::invalid_signature; + sodium_memzero(message.data(), message.size()); + return result; } } @@ -108,6 +150,46 @@ inline PayloadResult verify_payload(const std::string& signature, #pragma comment(lib, "bcrypt.lib") namespace keyauth_detail { +// Prefer UTF-8 paths while retaining existing Windows code-page callers. -nigel +inline std::filesystem::path native_file_path(const std::string& value) { + if (value.empty() || value.find('\0') != std::string::npos || value.size() > INT_MAX) { + throw std::invalid_argument("Invalid API file path."); + } + + const int length = static_cast(value.size()); + UINT code_page = CP_UTF8; + DWORD flags = MB_ERR_INVALID_CHARS; + int required = MultiByteToWideChar(code_page, flags, value.data(), length, nullptr, 0); + if (!required) { + if (GetLastError() != ERROR_NO_UNICODE_TRANSLATION) { + throw std::runtime_error("Cannot decode API file path."); + } + code_page = CP_ACP; + flags = 0; + required = MultiByteToWideChar(code_page, flags, value.data(), length, nullptr, 0); + } + if (!required) { + throw std::runtime_error("Cannot decode API file path."); + } + + std::wstring decoded(static_cast(required), L'\0'); + if (MultiByteToWideChar(code_page, flags, value.data(), length, decoded.data(), required) != required) { + throw std::runtime_error("Cannot decode API file path."); + } + std::filesystem::path result(decoded); + if (code_page == CP_UTF8 && GetACP() != CP_UTF8) { + std::error_code error; + if (!std::filesystem::exists(result, error)) { + const std::filesystem::path legacy(value); + error.clear(); + if (std::filesystem::exists(legacy, error)) { + return legacy; + } + } + } + return result; +} + // MD5 is retained solely for the API's existing executable/token hash fields. // Authentication is provided by TLS and Ed25519, not this compatibility digest. inline std::string file_md5(const std::filesystem::path& path) { @@ -199,11 +281,34 @@ inline ApiEndpoint parse_api_endpoint(const std::string& input) { throw std::invalid_argument("API URL must use a DNS hostname."); if (curl_url_set(handle.get(), CURLUPART_HOST, endpoint.host.c_str(), 0) != CURLUE_OK) throw std::invalid_argument("Invalid API hostname."); - endpoint.url = part(CURLUPART_URL); + if (curl_url_set(handle.get(), CURLUPART_SCHEME, "https", 0) != CURLUE_OK) + throw std::invalid_argument("Invalid API scheme."); + endpoint.url = part(CURLUPART_URL, CURLU_NO_DEFAULT_PORT); if (endpoint.url.empty()) throw std::invalid_argument("Invalid API URL."); return endpoint; } +inline std::string application_identity(const std::string& owner, const std::string& name, + const std::string& url) { + std::string identity; + const auto append = [&identity](const std::string& value) { + identity += std::to_string(value.size()); + identity += ':'; + identity += value; + }; + append(owner); + append(name); + append(parse_api_endpoint(url).url); + return identity; +} + +inline bool request_endpoint_matches(const std::string& base, const std::string& requested, + const std::string& operation) { + if (base == requested) return true; + return operation == "check" && requested == base + + (base.find('?') == std::string::npos ? "?" : "&") + "check_paid=1"; +} + inline bool hosts_line_overrides(std::string line, const std::string& host) { const auto comment = line.find('#'); if (comment != std::string::npos) line.resize(comment); @@ -310,6 +415,29 @@ inline void validate_api_reply(const nlohmann::json& data, const std::string& ow throw std::invalid_argument("Invalid API success code."); } +inline void validate_response_context(const nlohmann::json& data, const std::string& owner, + const std::string& name, const std::string& session, const std::string& operation) { + if (!data.is_object()) return; + const bool success = data.contains("success") && data["success"].is_boolean() && data["success"].get(); + const auto match = [&](const char* field, const std::string& expected, bool allow_error_null) { + if (!data.contains(field)) return; + const auto& value = data[field]; + if (!success && allow_error_null && value.is_null()) return; + if (!value.is_string() || value.get_ref() != expected) + throw std::invalid_argument(std::string("API response context mismatch: ") + field); + }; + match("ownerid", owner, false); + match("appname", name, true); + if (!data.contains("sessionid")) return; + const auto& value = data["sessionid"]; + if (operation == "init") { + if (success && (!value.is_string() || value.get_ref().empty())) + throw std::invalid_argument("Invalid initialization session identifier."); + } else if (!(operation == "logout" && success && value.is_string() && value.get_ref().empty())) { + match("sessionid", session, true); + } +} + inline bool magic_link_approved(const nlohmann::json& data) { if (!data.is_object() || !data.contains("success") || !data["success"].is_boolean() || !data["success"].get()) return false; @@ -343,8 +471,14 @@ inline std::string email_request(const std::string& type, const std::string& ema auto encode = [](const std::string& value) { if (value.empty()) return std::string(); if (value.size() > INT_MAX) throw std::invalid_argument("API input is too large."); - std::unique_ptr raw( - curl_easy_escape(nullptr, value.data(), static_cast(value.size())), curl_free); + const auto release = [](char* value) noexcept { + if (value) { + sodium_memzero(value, std::strlen(value)); + curl_free(value); + } + }; + std::unique_ptr raw( + curl_easy_escape(nullptr, value.data(), static_cast(value.size())), release); if (!raw) throw std::runtime_error("Could not encode API input."); return std::string(raw.get()); }; @@ -356,6 +490,21 @@ inline std::string email_request(const std::string& type, const std::string& ema return request; } +inline std::vector decode_download(const std::string& encoded) { + auto nibble = [](unsigned char c) -> unsigned char { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + throw std::invalid_argument("Download contents are not valid hexadecimal data."); + }; + if (encoded.size() % 2) throw std::invalid_argument("Download contents have an odd hexadecimal length."); + for (unsigned char c : encoded) (void)nibble(c); + std::vector decoded(encoded.size() / 2); + for (size_t i = 0; i < decoded.size(); ++i) + decoded[i] = static_cast((nibble(encoded[i * 2]) << 4) | nibble(encoded[i * 2 + 1])); + return decoded; +} + struct ResponseBuffer { std::string body; size_t limit; @@ -363,7 +512,7 @@ struct ResponseBuffer { }; inline bool append_response(ResponseBuffer& output, const char* bytes, size_t count) { - if (count > output.limit || output.body.size() > output.limit - count) { + if (output.limit && (count > output.limit || output.body.size() > output.limit - count)) { output.exceeded = true; return false; } @@ -404,6 +553,8 @@ namespace KeyAuth { void init(); void check(bool check_paid = false); bool authenticated(bool require_paid = false) const; + static std::string protect_data(const std::string& data); + static std::string unprotect_data(const std::string& data); void log(std::string msg); void license(std::string key, std::string code = ""); std::string var(std::string varid); @@ -440,7 +591,7 @@ namespace KeyAuth { bool block_proxy = false; bool require_signed_downloads = true; size_t max_api_response_bytes = 8ULL * 1024 * 1024; - size_t max_download_response_bytes = 512ULL * 1024 * 1024; + size_t max_download_response_bytes = 0; static std::string expiry_remaining(const std::string& expiry); static constexpr const char* kSavePath = "test.json"; static constexpr int kInitFailSleepMs = 1500; @@ -516,13 +667,13 @@ namespace KeyAuth { Tfa tfa; // Optional network hardening controls (do not require backend changes). - void set_allowed_hosts(const std::vector& hosts) { allowed_hosts = hosts; } - void add_allowed_host(const std::string& host) { allowed_hosts.push_back(host); } - void clear_allowed_hosts() { allowed_hosts.clear(); } + void set_allowed_hosts(const std::vector& hosts) { std::lock_guard lock(api_mutex_); allowed_hosts = hosts; } + void add_allowed_host(const std::string& host) { std::lock_guard lock(api_mutex_); allowed_hosts.push_back(host); } + void clear_allowed_hosts() { std::lock_guard lock(api_mutex_); allowed_hosts.clear(); } - void set_pinned_public_keys(const std::vector& pins) { pinned_public_keys = pins; } - void add_pinned_public_key(const std::string& pin) { pinned_public_keys.push_back(pin); } - void clear_pinned_public_keys() { pinned_public_keys.clear(); } + void set_pinned_public_keys(const std::vector& pins) { std::lock_guard lock(api_mutex_); pinned_public_keys = pins; } + void add_pinned_public_key(const std::string& pin) { std::lock_guard lock(api_mutex_); pinned_public_keys.push_back(pin); } + void clear_pinned_public_keys() { std::lock_guard lock(api_mutex_); pinned_public_keys.clear(); } // Optional in-memory string protection. When enabled, public fields like // name/ownerid/version/url/path are wiped and only decrypted on demand. @@ -530,6 +681,9 @@ namespace KeyAuth { private: void checkInit(); + std::string initialization_context() const; + void bind_initialization_context(); + bool initialization_context_valid() const; void start_heartbeat(); bool initialized = false; std::atomic LoggedIn{false}; @@ -539,7 +693,7 @@ namespace KeyAuth { bool heartbeat_stop_ = false; std::thread heartbeat_thread_; std::string sessionid, enckey; - std::string authenticated_host_; + std::string initialized_context_; std::string magic_link_email_; std::chrono::steady_clock::time_point magic_link_started_{}; std::chrono::steady_clock::time_point magic_link_last_check_{}; @@ -565,6 +719,7 @@ namespace KeyAuth { void refresh_auth_runtime(); bool local_auth_valid(bool require_paid = false, bool require_fresh = true) const; bool has_active_subscription() const; + void store_auth_entitlements(bool reset_profile); uint64_t compute_auth_seal(uint64_t nonce, long long window) const; std::string req(std::string data, const std::string& url); @@ -622,7 +777,10 @@ namespace KeyAuth { api::response.message = data["message"]; api::response.isPaid = false; - if (data.contains(XorStr("role").c_str()) && data[XorStr("role")] != XorStr("tester").c_str() && data[XorStr("role")] != XorStr("not_checked").c_str()) { + if (api::response.success && data.contains(XorStr("role").c_str()) && + data[XorStr("role")].is_string() && + data[XorStr("role")].get_ref().find_first_not_of(" \t\r\n") != std::string::npos && + data[XorStr("role")] != XorStr("tester").c_str() && data[XorStr("role")] != XorStr("not_checked").c_str()) { api::response.isPaid = true; } } diff --git a/curl/curl.h b/curl/curl.h index 183488a..ccc9a09 100644 --- a/curl/curl.h +++ b/curl/curl.h @@ -30,18 +30,19 @@ */ #ifdef CURL_NO_OLDIES -#define CURL_STRICTER +#define CURL_STRICTER /* not used since 8.11.0 */ #endif /* Compile-time deprecation macros. */ -#if (defined(__GNUC__) && \ - ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) || \ - defined(__IAR_SYSTEMS_ICC__)) && \ - !defined(__INTEL_COMPILER) && \ +#if (defined(__GNUC__) && \ + ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1))) || \ + (defined(__clang__) && __clang_major__ >= 3) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + !defined(__INTEL_COMPILER) && \ !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL) #define CURL_DEPRECATED(version, message) \ __attribute__((deprecated("since " # version ". " message))) -#if defined(__IAR_SYSTEMS_ICC__) +#ifdef __IAR_SYSTEMS_ICC__ #define CURL_IGNORE_DEPRECATION(statements) \ _Pragma("diag_suppress=Pe1444") \ statements \ @@ -58,7 +59,7 @@ #define CURL_IGNORE_DEPRECATION(statements) statements #endif -#include "curlver.h" /* libcurl version defines */ +#include "curlver.h" /* libcurl version defines */ #include "system.h" /* determine things runtime */ #include @@ -92,45 +93,40 @@ defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \ (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \ - defined(__sun__) || defined(__serenity__) || defined(__vxworks__) + defined(__sun__) || defined(__serenity__) || defined(__vxworks__) || \ + (defined(__linux__) && defined(_POSIX_C_SOURCE) && \ + _POSIX_C_SOURCE >= 200112L) #include #endif -#if !defined(_WIN32) && !defined(_WIN32_WCE) +#ifndef _WIN32 #include -#endif - -#if !defined(_WIN32) #include #endif -/* Compatibility for non-Clang compilers */ -#ifndef __has_declspec_attribute -# define __has_declspec_attribute(x) 0 -#endif - -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif -#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) -typedef struct Curl_easy CURL; -typedef struct Curl_share CURLSH; -#else typedef void CURL; typedef void CURLSH; -#endif /* * libcurl external API function linkage decorations. */ -#if 1 +#ifdef __has_declspec_attribute +#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) __has_declspec_attribute(x) +#else +#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) 0 +#endif + +#ifdef CURL_STATICLIB # define CURL_EXTERN #elif defined(_WIN32) || \ - (__has_declspec_attribute(dllexport) && \ - __has_declspec_attribute(dllimport)) -# if defined(BUILDING_LIBCURL) + (CURL_HAS_DECLSPEC_ATTRIBUTE(dllexport) && \ + CURL_HAS_DECLSPEC_ATTRIBUTE(dllimport)) +# ifdef BUILDING_LIBCURL # define CURL_EXTERN __declspec(dllexport) # else # define CURL_EXTERN __declspec(dllimport) @@ -148,7 +144,7 @@ typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else typedef int curl_socket_t; -#define CURL_SOCKET_BAD -1 +#define CURL_SOCKET_BAD (-1) #endif #define curl_socket_typedef #endif /* curl_socket_typedef */ @@ -164,11 +160,11 @@ typedef enum { CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, CURLSSLBACKEND_WOLFSSL = 7, CURLSSLBACKEND_SCHANNEL = 8, - CURLSSLBACKEND_SECURETRANSPORT = 9, + CURLSSLBACKEND_SECURETRANSPORT CURL_DEPRECATED(8.15.0, "") = 9, CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10, CURLSSLBACKEND_MBEDTLS = 11, CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12, - CURLSSLBACKEND_BEARSSL = 13, + CURLSSLBACKEND_BEARSSL CURL_DEPRECATED(8.15.0, "") = 13, CURLSSLBACKEND_RUSTLS = 14 } curl_sslbackend; @@ -181,6 +177,16 @@ typedef enum { #define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL #define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT +/* bits for the CURLOPT_FOLLOWLOCATION option */ +#define CURLFOLLOW_ALL 1L /* generic follow redirects */ + +/* Do not use the custom method in the follow-up request if the HTTP code + instructs so (301, 302, 303). */ +#define CURLFOLLOW_OBEYCODE 2L + +/* Only use the custom method in the first request, always reset in the next */ +#define CURLFOLLOW_FIRSTONLY 3L + struct curl_httppost { struct curl_httppost *next; /* next entry in the list */ char *name; /* pointer to allocated name */ @@ -198,26 +204,26 @@ struct curl_httppost { long flags; /* as defined below */ /* specified content is a filename */ -#define CURL_HTTPPOST_FILENAME (1<<0) +#define CURL_HTTPPOST_FILENAME (1 << 0) /* specified content is a filename */ -#define CURL_HTTPPOST_READFILE (1<<1) +#define CURL_HTTPPOST_READFILE (1 << 1) /* name is only stored pointer do not free in formfree */ -#define CURL_HTTPPOST_PTRNAME (1<<2) +#define CURL_HTTPPOST_PTRNAME (1 << 2) /* contents is only stored pointer do not free in formfree */ -#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +#define CURL_HTTPPOST_PTRCONTENTS (1 << 3) /* upload file from buffer */ -#define CURL_HTTPPOST_BUFFER (1<<4) +#define CURL_HTTPPOST_BUFFER (1 << 4) /* upload file from pointer contents */ -#define CURL_HTTPPOST_PTRBUFFER (1<<5) +#define CURL_HTTPPOST_PTRBUFFER (1 << 5) /* upload file contents by using the regular read callback to get the data and pass the given pointer as custom pointer */ -#define CURL_HTTPPOST_CALLBACK (1<<6) +#define CURL_HTTPPOST_CALLBACK (1 << 6) /* use size in 'contentlen', added in 7.46.0 */ -#define CURL_HTTPPOST_LARGE (1<<7) +#define CURL_HTTPPOST_LARGE (1 << 7) char *showfilename; /* The filename to show. If not set, the - actual filename will be used (if this - is a file part) */ + actual filename is used (if this is + a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ curl_off_t contentlen; /* alternative length of contents @@ -225,9 +231,8 @@ struct curl_httppost { set. Added in 7.46.0 */ }; - -/* This is a return code for the progress callback that, when returned, will - signal libcurl to continue executing the default progress function */ +/* This is a return code for the progress callback that, when returned, + signals libcurl to continue executing the default progress function */ #define CURL_PROGRESSFUNC_CONTINUE 0x10000001 /* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now @@ -249,32 +254,32 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE (10*1024*1024) +#define CURL_MAX_READ_SIZE (10 * 1024 * 1024) #endif #ifndef CURL_MAX_WRITE_SIZE - /* Tests have proven that 20K is a very bad buffer size for uploads on - Windows, while 16K for some odd reason performed a lot better. - We do the ifndef check to allow this value to easier be changed at build - time for those who feel adventurous. The practical minimum is about - 400 bytes since libcurl uses a buffer of this size as a scratch area - (unrelated to network send operations). */ + /* Tests have proven that 20K is a bad buffer size for uploads on Windows, + while 16K for some odd reason performed a lot better. We do the ifndef + check to allow this value to easier be changed at build time for those + who feel adventurous. The practical minimum is about 400 bytes since + libcurl uses a buffer of this size as a scratch area (unrelated to + network send operations). */ #define CURL_MAX_WRITE_SIZE 16384 #endif #ifndef CURL_MAX_HTTP_HEADER /* The only reason to have a max limit for this is to avoid the risk of a bad - server feeding libcurl with a never-ending header that will cause reallocs + server feeding libcurl with a never-ending header that causes reallocs infinitely */ -#define CURL_MAX_HTTP_HEADER (100*1024) +#define CURL_MAX_HTTP_HEADER (100 * 1024) #endif /* This is a magic return code for the write callback that, when returned, - will signal libcurl to pause receiving on the current transfer. */ + signals libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 /* This is a magic return code for the write callback that, when returned, - will signal an error from the callback. */ + signals an error from the callback. */ #define CURL_WRITEFUNC_ERROR 0xFFFFFFFF typedef size_t (*curl_write_callback)(char *buffer, @@ -282,7 +287,7 @@ typedef size_t (*curl_write_callback)(char *buffer, size_t nitems, void *outstream); -/* This callback will be called when a new resolver request is made */ +/* This callback is called when a new resolver request is made */ typedef int (*curl_resolver_start_callback)(void *resolver_state, void *reserved, void *userdata); @@ -300,14 +305,14 @@ typedef enum { CURLFILETYPE_UNKNOWN /* should never occur */ } curlfiletype; -#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) -#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) -#define CURLFINFOFLAG_KNOWN_TIME (1<<2) -#define CURLFINFOFLAG_KNOWN_PERM (1<<3) -#define CURLFINFOFLAG_KNOWN_UID (1<<4) -#define CURLFINFOFLAG_KNOWN_GID (1<<5) -#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) -#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) +#define CURLFINFOFLAG_KNOWN_FILENAME (1 << 0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1 << 1) +#define CURLFINFOFLAG_KNOWN_TIME (1 << 2) +#define CURLFINFOFLAG_KNOWN_PERM (1 << 3) +#define CURLFINFOFLAG_KNOWN_UID (1 << 4) +#define CURLFINFOFLAG_KNOWN_GID (1 << 5) +#define CURLFINFOFLAG_KNOWN_SIZE (1 << 6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1 << 7) /* Information about a single file, used when doing FTP wildcard matching */ struct curl_fileinfo { @@ -382,11 +387,11 @@ typedef int (*curl_seek_callback)(void *instream, curl_off_t offset, int origin); /* 'whence' */ -/* This is a return code for the read callback that, when returned, will - signal libcurl to immediately abort the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to immediately abort the current transfer. */ #define CURL_READFUNC_ABORT 0x10000000 -/* This is a return code for the read callback that, when returned, will - signal libcurl to pause sending data on the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to pause sending data on the current transfer. */ #define CURL_READFUNC_PAUSE 0x10000001 /* Return code for when the trailing headers' callback has terminated @@ -397,12 +402,12 @@ typedef int (*curl_seek_callback)(void *instream, #define CURL_TRAILERFUNC_ABORT 1 typedef size_t (*curl_read_callback)(char *buffer, - size_t size, - size_t nitems, - void *instream); + size_t size, + size_t nitems, + void *instream); typedef int (*curl_trailer_callback)(struct curl_slist **list, - void *userdata); + void *userdata); typedef enum { CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ @@ -555,14 +560,14 @@ typedef enum { CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ CURLE_OBSOLETE32, /* 32 - NOT USED */ CURLE_RANGE_ERROR, /* 33 - RANGE "command" did not work */ - CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_OBSOLETE34, /* 34 */ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ CURLE_BAD_DOWNLOAD_RESUME, /* 36 - could not resume download */ CURLE_FILE_COULDNT_READ_FILE, /* 37 */ CURLE_LDAP_CANNOT_BIND, /* 38 */ CURLE_LDAP_SEARCH_FAILED, /* 39 */ CURLE_OBSOLETE40, /* 40 - NOT USED */ - CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ + CURLE_OBSOLETE41, /* 41 - NOT USED starting with 7.53.0 */ CURLE_ABORTED_BY_CALLBACK, /* 42 */ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ CURLE_OBSOLETE44, /* 44 - NOT USED */ @@ -590,7 +595,7 @@ typedef enum { CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind that failed */ - CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialize ENGINE */ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not accepted and we failed to login */ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ @@ -606,7 +611,7 @@ typedef enum { or wrong format */ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ CURLE_SSH, /* 79 - error from the SSH layer, somewhat - generic so the error message will be of + generic so the error message is of interest when this has happened */ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL @@ -624,12 +629,12 @@ typedef enum { CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the - session will be queued */ + session is queued */ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not match */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer - */ + */ CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from inside a callback */ CURLE_AUTH_ERROR, /* 94 - an authentication function returned an @@ -647,6 +652,12 @@ typedef enum { #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ +/* removed in 7.53.0 */ +#define CURLE_FUNCTION_NOT_FOUND CURLE_OBSOLETE41 + +/* removed in 7.56.0 */ +#define CURLE_HTTP_POST_ERROR CURLE_OBSOLETE34 + /* Previously obsolete error code reused in 7.38.0 */ #define CURLE_OBSOLETE16 CURLE_HTTP2 @@ -778,20 +789,26 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ mbedtls_ssl_config */ void *userptr); +#define CURLPROXY_HTTP 0L /* added in 7.10, new in 7.19.4 default is + to use CONNECT HTTP/1.1 */ +#define CURLPROXY_HTTP_1_0 1L /* force to use CONNECT HTTP/1.0 + added in 7.19.4 */ +#define CURLPROXY_HTTPS 2L /* HTTPS but stick to HTTP/1 + added in 7.52.0 */ +#define CURLPROXY_HTTPS2 3L /* HTTPS and attempt HTTP/2 + added in 8.2.0 */ +#define CURLPROXY_SOCKS4 4L /* support added in 7.15.2, enum existed + already in 7.10 */ +#define CURLPROXY_SOCKS5 5L /* added in 7.10 */ +#define CURLPROXY_SOCKS4A 6L /* added in 7.18.0 */ +#define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along + the hostname rather than the IP + address. added in 7.18.0 */ +#define CURLPROXY_HTTPS3 8L /* HTTPS and attempt HTTP/3 + added in 8.21.0 */ + typedef enum { - CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use - CONNECT HTTP/1.1 */ - CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT - HTTP/1.0 */ - CURLPROXY_HTTPS = 2, /* HTTPS but stick to HTTP/1 added in 7.52.0 */ - CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */ - CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already - in 7.10 */ - CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ - CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ - CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the - hostname rather than the IP address. added - in 7.18.0 */ + CURLPROXY_LAST = 9 /* never use */ } curl_proxytype; /* this enum was added in 7.10 */ /* @@ -803,48 +820,59 @@ typedef enum { * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) * CURLAUTH_NTLM - HTTP NTLM authentication - * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavor * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper * CURLAUTH_BEARER - HTTP Bearer token authentication * CURLAUTH_ONLY - Use together with a single other type to force no - * authentication or just that single type + * authentication or that single type * CURLAUTH_ANY - All fine types set * CURLAUTH_ANYSAFE - All fine types except Basic */ #define CURLAUTH_NONE ((unsigned long)0) -#define CURLAUTH_BASIC (((unsigned long)1)<<0) -#define CURLAUTH_DIGEST (((unsigned long)1)<<1) -#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +#define CURLAUTH_BASIC (((unsigned long)1) << 0) +#define CURLAUTH_DIGEST (((unsigned long)1) << 1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1) << 2) /* Deprecated since the advent of CURLAUTH_NEGOTIATE */ #define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE /* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ #define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE -#define CURLAUTH_NTLM (((unsigned long)1)<<3) -#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM (((unsigned long)1) << 3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1) << 4) #ifndef CURL_NO_OLDIES /* functionality removed since 8.8.0 */ -#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_NTLM_WB (((unsigned long)1) << 5) #endif -#define CURLAUTH_BEARER (((unsigned long)1)<<6) -#define CURLAUTH_AWS_SIGV4 (((unsigned long)1)<<7) -#define CURLAUTH_ONLY (((unsigned long)1)<<31) -#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) -#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) - -#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ -#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ -#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ -#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ -#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ -#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ -#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ -#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ -#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY - -#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ -#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ -#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ +#define CURLAUTH_BEARER (((unsigned long)1) << 6) +#define CURLAUTH_AWS_SIGV4 (((unsigned long)1) << 7) +#define CURLAUTH_HTTPSIG (((unsigned long)1) << 8) +#define CURLAUTH_ONLY (((unsigned long)1) << 31) +#define CURLAUTH_ANY ((~(CURLAUTH_DIGEST_IE | \ + CURLAUTH_HTTPSIG)) & \ + ((unsigned long)0xffffffff)) +#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE | \ + CURLAUTH_HTTPSIG)) & \ + ((unsigned long)0xffffffff)) + +/* constants for CURLOPT_HTTPSIG_ALGORITHM */ +#define CURLHTTPSIG_NONE 0L +#define CURLHTTPSIG_ED25519 1L +#define CURLHTTPSIG_HMAC_SHA256 2L + +/* all types supported by server */ +#define CURLSSH_AUTH_ANY ((unsigned long)0xffffffff) +#define CURLSSH_AUTH_NONE 0L /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1L << 0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1L << 1) /* password */ +#define CURLSSH_AUTH_HOST (1L << 2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1L << 3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1L << 4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1L << 5) /* gssapi (kerberos, ...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURLGSSAPI_DELEGATION_NONE 0L /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1L<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1L<<1) /* delegate always */ #define CURL_ERROR_SIZE 256 @@ -872,7 +900,7 @@ enum curl_khstat { CURLKHSTAT_REJECT, /* reject the connection, return an error */ CURLKHSTAT_DEFER, /* do not accept it, but we cannot answer right now. Causes a CURLE_PEER_FAILED_VERIFICATION error but the - connection will be left intact etc */ + connection is left intact etc */ CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ }; @@ -886,61 +914,64 @@ enum curl_khmatch { }; typedef int - (*curl_sshkeycallback) (CURL *easy, /* easy handle */ - const struct curl_khkey *knownkey, /* known */ - const struct curl_khkey *foundkey, /* found */ - enum curl_khmatch, /* libcurl's view on the keys */ - void *clientp); /* custom pointer passed with */ - /* CURLOPT_SSH_KEYDATA */ + (*curl_sshkeycallback)(CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed with */ + /* CURLOPT_SSH_KEYDATA */ typedef int - (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ - /* with CURLOPT_SSH_HOSTKEYDATA */ + (*curl_sshhostkeycallback)(void *clientp,/* custom pointer passed */ + /* with CURLOPT_SSH_HOSTKEYDATA */ int keytype, /* CURLKHTYPE */ const char *key, /* hostkey to check */ size_t keylen); /* length of the key */ /* return CURLE_OK to accept */ /* or something else to refuse */ - /* parameter for the CURLOPT_USE_SSL option */ +#define CURLUSESSL_NONE 0L /* do not attempt to use SSL */ +#define CURLUSESSL_TRY 1L /* try using SSL, proceed anyway otherwise */ +#define CURLUSESSL_CONTROL 2L /* SSL for the control connection or fail */ +#define CURLUSESSL_ALL 3L /* SSL for all communication or fail */ + typedef enum { - CURLUSESSL_NONE, /* do not attempt to use SSL */ - CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ - CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ - CURLUSESSL_ALL, /* SSL for all communication or fail */ - CURLUSESSL_LAST /* not an option, never use */ + CURLUSESSL_LAST = 4 /* not an option, never use */ } curl_usessl; /* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ /* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the name of improving interoperability with older servers. Some SSL libraries - have introduced work-arounds for this flaw but those work-arounds sometimes + have introduced workarounds for this flaw but those workarounds sometimes make the SSL communication fail. To regain functionality with those broken servers, a user can this way allow the vulnerability back. */ -#define CURLSSLOPT_ALLOW_BEAST (1<<0) +#define CURLSSLOPT_ALLOW_BEAST (1L << 0) /* - NO_REVOKE tells libcurl to disable certificate revocation checks for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_NO_REVOKE (1<<1) +#define CURLSSLOPT_NO_REVOKE (1L << 1) /* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain if possible. The OpenSSL backend has this ability. */ -#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2) +#define CURLSSLOPT_NO_PARTIALCHAIN (1L << 2) /* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline checks and ignore missing revocation list for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_REVOKE_BEST_EFFORT (1<<3) +#define CURLSSLOPT_REVOKE_BEST_EFFORT (1L << 3) /* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of operating system. Currently implemented under MS-Windows. */ -#define CURLSSLOPT_NATIVE_CA (1<<4) +#define CURLSSLOPT_NATIVE_CA (1L << 4) /* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use a client certificate for authentication. (Schannel) */ -#define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5) +#define CURLSSLOPT_AUTO_CLIENT_CERT (1L << 5) + +/* If possible, send data using TLS 1.3 early data */ +#define CURLSSLOPT_EARLYDATA (1L << 6) /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document @@ -965,51 +996,62 @@ typedef enum { #endif /* !CURL_NO_OLDIES */ /* parameter for the CURLOPT_FTP_SSL_CCC option */ +#define CURLFTPSSL_CCC_NONE 0L /* do not send CCC */ +#define CURLFTPSSL_CCC_PASSIVE 1L /* Let the server initiate the shutdown */ +#define CURLFTPSSL_CCC_ACTIVE 2L /* Initiate the shutdown */ + typedef enum { - CURLFTPSSL_CCC_NONE, /* do not send CCC */ - CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ - CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ - CURLFTPSSL_CCC_LAST /* not an option, never use */ + CURLFTPSSL_CCC_LAST = 3 /* not an option, never use */ } curl_ftpccc; /* parameter for the CURLOPT_FTPSSLAUTH option */ +#define CURLFTPAUTH_DEFAULT 0L /* let libcurl decide */ +#define CURLFTPAUTH_SSL 1L /* use "AUTH SSL" */ +#define CURLFTPAUTH_TLS 2L /* use "AUTH TLS" */ + typedef enum { - CURLFTPAUTH_DEFAULT, /* let libcurl decide */ - CURLFTPAUTH_SSL, /* use "AUTH SSL" */ - CURLFTPAUTH_TLS, /* use "AUTH TLS" */ - CURLFTPAUTH_LAST /* not an option, never use */ + CURLFTPAUTH_LAST = 3 /* not an option, never use */ } curl_ftpauth; /* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +#define CURLFTP_CREATE_DIR_NONE 0L /* do NOT create missing dirs! */ +#define CURLFTP_CREATE_DIR 1L /* (FTP/SFTP) if CWD fails, try MKD and + then CWD again if MKD succeeded, for + SFTP this does similar magic */ +#define CURLFTP_CREATE_DIR_RETRY 2L /* (FTP only) if CWD fails, try MKD and + then CWD again even if MKD failed! */ + typedef enum { - CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ - CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD - again if MKD succeeded, for SFTP this does - similar magic */ - CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD - again even if MKD failed! */ - CURLFTP_CREATE_DIR_LAST /* not an option, never use */ + CURLFTP_CREATE_DIR_LAST = 3 /* not an option, never use */ } curl_ftpcreatedir; /* parameter for the CURLOPT_FTP_FILEMETHOD option */ +#define CURLFTPMETHOD_DEFAULT 0L /* let libcurl pick */ +#define CURLFTPMETHOD_MULTICWD 1L /* single CWD operation for each path + part */ +#define CURLFTPMETHOD_NOCWD 2L /* no CWD at all */ +#define CURLFTPMETHOD_SINGLECWD 3L /* one CWD to full dir, then work on file */ + typedef enum { - CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ - CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ - CURLFTPMETHOD_NOCWD, /* no CWD at all */ - CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ - CURLFTPMETHOD_LAST /* not an option, never use */ + CURLFTPMETHOD_LAST = 4 /* not an option, never use */ } curl_ftpmethod; /* bitmask defines for CURLOPT_HEADEROPT */ -#define CURLHEADER_UNIFIED 0 -#define CURLHEADER_SEPARATE (1<<0) +#define CURLHEADER_UNIFIED 0L +#define CURLHEADER_SEPARATE (1L << 0) /* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */ -#define CURLALTSVC_READONLYFILE (1<<2) -#define CURLALTSVC_H1 (1<<3) -#define CURLALTSVC_H2 (1<<4) -#define CURLALTSVC_H3 (1<<5) - +#define CURLALTSVC_READONLYFILE (1L << 2) +#define CURLALTSVC_H1 (1L << 3) +#define CURLALTSVC_H2 (1L << 4) +#define CURLALTSVC_H3 (1L << 5) + +/* bitmask values for CURLOPT_UPLOAD_FLAGS */ +#define CURLULFLAG_ANSWERED (1L << 0) +#define CURLULFLAG_DELETED (1L << 1) +#define CURLULFLAG_DRAFT (1L << 2) +#define CURLULFLAG_FLAGGED (1L << 3) +#define CURLULFLAG_SEEN (1L << 4) struct curl_hstsentry { char *name; @@ -1038,42 +1080,43 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, void *userp); /* CURLHSTS_* are bits for the CURLOPT_HSTS option */ -#define CURLHSTS_ENABLE (long)(1<<0) -#define CURLHSTS_READONLYFILE (long)(1<<1) +#define CURLHSTS_ENABLE (1L << 0) +#define CURLHSTS_READONLYFILE (1L << 1) /* The CURLPROTO_ defines below are for the **deprecated** CURLOPT_*PROTOCOLS options. Do not use. */ -#define CURLPROTO_HTTP (1<<0) -#define CURLPROTO_HTTPS (1<<1) -#define CURLPROTO_FTP (1<<2) -#define CURLPROTO_FTPS (1<<3) -#define CURLPROTO_SCP (1<<4) -#define CURLPROTO_SFTP (1<<5) -#define CURLPROTO_TELNET (1<<6) -#define CURLPROTO_LDAP (1<<7) -#define CURLPROTO_LDAPS (1<<8) -#define CURLPROTO_DICT (1<<9) -#define CURLPROTO_FILE (1<<10) -#define CURLPROTO_TFTP (1<<11) -#define CURLPROTO_IMAP (1<<12) -#define CURLPROTO_IMAPS (1<<13) -#define CURLPROTO_POP3 (1<<14) -#define CURLPROTO_POP3S (1<<15) -#define CURLPROTO_SMTP (1<<16) -#define CURLPROTO_SMTPS (1<<17) -#define CURLPROTO_RTSP (1<<18) -#define CURLPROTO_RTMP (1<<19) -#define CURLPROTO_RTMPT (1<<20) -#define CURLPROTO_RTMPE (1<<21) -#define CURLPROTO_RTMPTE (1<<22) -#define CURLPROTO_RTMPS (1<<23) -#define CURLPROTO_RTMPTS (1<<24) -#define CURLPROTO_GOPHER (1<<25) -#define CURLPROTO_SMB (1<<26) -#define CURLPROTO_SMBS (1<<27) -#define CURLPROTO_MQTT (1<<28) -#define CURLPROTO_GOPHERS (1<<29) -#define CURLPROTO_ALL (~0) /* enable everything */ +#define CURLPROTO_HTTP (1L << 0) +#define CURLPROTO_HTTPS (1L << 1) +#define CURLPROTO_FTP (1L << 2) +#define CURLPROTO_FTPS (1L << 3) +#define CURLPROTO_SCP (1L << 4) +#define CURLPROTO_SFTP (1L << 5) +#define CURLPROTO_TELNET (1L << 6) +#define CURLPROTO_LDAP (1L << 7) +#define CURLPROTO_LDAPS (1L << 8) +#define CURLPROTO_DICT (1L << 9) +#define CURLPROTO_FILE (1L << 10) +#define CURLPROTO_TFTP (1L << 11) +#define CURLPROTO_IMAP (1L << 12) +#define CURLPROTO_IMAPS (1L << 13) +#define CURLPROTO_POP3 (1L << 14) +#define CURLPROTO_POP3S (1L << 15) +#define CURLPROTO_SMTP (1L << 16) +#define CURLPROTO_SMTPS (1L << 17) +#define CURLPROTO_RTSP (1L << 18) +#define CURLPROTO_RTMP (1L << 19) +#define CURLPROTO_RTMPT (1L << 20) +#define CURLPROTO_RTMPE (1L << 21) +#define CURLPROTO_RTMPTE (1L << 22) +#define CURLPROTO_RTMPS (1L << 23) +#define CURLPROTO_RTMPTS (1L << 24) +#define CURLPROTO_GOPHER (1L << 25) +#define CURLPROTO_SMB (1L << 26) +#define CURLPROTO_SMBS (1L << 27) +#define CURLPROTO_MQTT (1L << 28) +#define CURLPROTO_GOPHERS (1L << 29) +#define CURLPROTO_ALL ((unsigned long)0xffffffff) /* old-style enable + "everything" */ /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ @@ -1086,13 +1129,13 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the string options from the header file */ - -#define CURLOPT(na,t,nu) na = t + nu -#define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu +#define CURLOPT(na, t, nu) na = ((t) + (nu)) +#define CURLOPTDEPRECATED(na, t, nu, v, m) na CURL_DEPRECATED(v, m) \ + = ((t) + (nu)) /* CURLOPT aliases that make no runtime difference */ -/* 'char *' argument to a string with a trailing zero */ +/* 'char *' argument to a string with a null-terminator */ #define CURLOPTTYPE_STRINGPOINT CURLOPTTYPE_OBJECTPOINT /* 'struct curl_slist *' argument */ @@ -1139,12 +1182,12 @@ typedef enum { * bytes big. */ CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), - /* Function that will be called to store the output (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store the output (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), - /* Function that will be called to read the input (instead of fread). The - * parameters will use fread() syntax, make sure to follow them. */ + /* Function that is called to read the input (instead of fread). The + * parameters use fread() syntax, make sure to follow them. */ CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ @@ -1218,7 +1261,7 @@ typedef enum { CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), /* send FILE * or void * to store headers to, if you use a callback it - is simply passed to the callback unmodified */ + is passed to the callback unmodified */ CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_CBPOINT, 29), /* point to a file to read the initial cookies from, also enables @@ -1298,7 +1341,7 @@ typedef enum { /* 55 = OBSOLETE */ /* DEPRECATED - * Function that will be called instead of the internal progress display + * Function that is called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, @@ -1327,10 +1370,11 @@ typedef enum { /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but does not match one of these, 'private' will be used. */ - CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), + * is set but does not match one of these, 'private' is used. */ + CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, + 8.17.0, "removed"), - /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + /* Set if we should verify the peer in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate @@ -1379,8 +1423,8 @@ typedef enum { OK within this time, then fine... This only aborts the connect phase. */ CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), - /* Function that will be called to store headers (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store headers (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable @@ -1388,7 +1432,7 @@ typedef enum { */ CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), - /* Set if we should verify the Common name from the peer certificate in ssl + /* Set if we should verify the Common name from the peer certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), @@ -1405,7 +1449,7 @@ typedef enum { CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84), /* Specifically switch on or off the FTP engine's use of the EPSV command. By - default, that one will always be attempted before the more traditional + default, that one is always attempted before the more traditional PASV command. */ CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), @@ -1462,8 +1506,8 @@ typedef enum { CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and - CURLPROXY_SOCKS5. */ + CURLPROXY_HTTPS, CURLPROXY_HTTPS2, CURLPROXY_HTTPS3, CURLPROXY_SOCKS4, + CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like @@ -1483,7 +1527,7 @@ typedef enum { CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), /* Specifically switch on or off the FTP engine's use of the EPRT command ( - it also disables the LPRT attempt). By default, those ones will always be + it also disables the LPRT attempt). By default, those ones are always attempted before the good old traditional PORT command. */ CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), @@ -1492,19 +1536,18 @@ typedef enum { Note that setting multiple bits may cause extra network round-trips. */ CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), - /* Set the ssl context callback function, currently only for OpenSSL or + /* Set the SSL context callback function, currently only for OpenSSL or wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. The function must match the curl_ssl_ctx_callback prototype. */ CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), - /* Set the userdata for the ssl context callback function's third + /* Set the userdata for the SSL context callback function's third argument */ CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109), /* FTP Option that causes missing dirs to be created on the remote server. In 7.19.4 we introduced the convenience enums for this option using the - CURLFTP_CREATE_DIR prefix. - */ + CURLFTP_CREATE_DIR prefix. */ CURLOPT(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110), /* Set this to a bitmask value to enable the particular authentications @@ -1523,7 +1566,7 @@ typedef enum { systems with support for more than one, i.e IPv4 _and_ IPv6. */ CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113), - /* Set this option to limit the size of a file that will be downloaded from + /* Set this option to limit the size of a file that is to be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for @@ -1540,13 +1583,13 @@ typedef enum { */ CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), - /* Sets the maximum size of data that will be downloaded from + /* Sets the maximum size of data that is to be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the filename of your .netrc file you want libcurl - to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + to parse (using the CURLOPT_NETRC option). If not set, libcurl does a poor attempt to find the user's home directory and check for a .netrc file in there. */ CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), @@ -1555,7 +1598,7 @@ typedef enum { CURLUSESSL_TRY - try using SSL, proceed anyway otherwise CURLUSESSL_CONTROL - SSL for the control connection or fail CURLUSESSL_ALL - SSL for all communication or fail - */ + */ CURLOPT(CURLOPT_USE_SSL, CURLOPTTYPE_VALUES, 119), /* The _LARGE version of the standard POSTFIELDSIZE option */ @@ -1581,7 +1624,7 @@ typedef enum { CURLFTPAUTH_DEFAULT - let libcurl decide CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL - */ + */ CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_VALUES, 129), CURLOPTDEPRECATED(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130, @@ -1604,7 +1647,7 @@ typedef enum { /* Set to non-zero to skip the IP address received in a 227 PASV FTP server response. Typically used for FTP-SSL purposes but is not restricted to - that. libcurl will then instead use the same IP address it used for the + that. libcurl is then instead using the same IP address it used for the control connection. */ CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), @@ -1616,27 +1659,26 @@ typedef enum { CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 will make no additional attempts but the first. - */ + Thus, setting it to 1 makes no additional attempts but the first. */ CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), /* no transfer, set up connection and let application use the socket by extracting it with CURLINFO_LASTSOCKET */ CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), - /* Function that will be called to convert from the + /* Function that is called to convert from the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert to the + /* Function that is called to convert to the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert from UTF8 + /* Function that is called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, @@ -1730,9 +1772,9 @@ typedef enum { /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For - example, local.com will match local.com and www.local.com, but NOT + example, local.com matches local.com and www.local.com, but NOT notlocal.com or www.notlocal.com. For compatibility with other - implementations of this, .local.com will be considered to be the same as + implementations of this, .local.com is considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), @@ -1829,24 +1871,25 @@ typedef enum { CURLOPT(CURLOPT_RESOLVE, CURLOPTTYPE_SLISTPOINT, 203), /* Set a username for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204, + 8.22.0, "Support was removed"), /* Set a password for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205, + 8.22.0, "Support was removed"), /* Set authentication type for authenticated TLS */ - CURLOPT(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206), + CURLOPTDEPRECATED(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206, + 8.22.0, "Support was removed"), /* Set to 1 to enable the "TE:" header in HTTP requests to ask for compressed transfer-encoded responses. Set to 0 to disable the use of TE: in outgoing requests. The current default is 0, but it might change in a future libcurl release. - libcurl will ask for the compressed methods it knows of, and if that - is not any, it will not ask for transfer-encoding at all even if this - option is set to 1. - - */ + libcurl asks for the compressed methods it knows of, and if that + is not any, it does not ask for transfer-encoding at all even if this + option is set to 1. */ CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), /* Callback function for closing socket (instead of close(2)). The callback @@ -1881,7 +1924,7 @@ typedef enum { /* Enable/disable SASL initial response */ CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), - /* Function that will be called instead of the internal progress display + /* Function that is called instead of the internal progress display * function. This function should be defined as the curl_xferinfo_callback * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), @@ -1905,11 +1948,11 @@ typedef enum { /* Set authentication options directly */ CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), - /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS NPN extension (http2 over SSL might fail without) */ CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, 7.86.0, "Has no function"), - /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS ALPN extension (http2 over SSL might fail without) */ CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), /* Time to wait for a response to an HTTP request containing an @@ -1923,8 +1966,7 @@ typedef enum { /* Pass in a bitmask of "header options" */ CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_VALUES, 229), - /* The public key in DER form used to validate the peer public key - this option is used only if SSL_VERIFYPEER is true */ + /* The public key used to validate the peer public key */ CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230), /* Path to Unix domain socket */ @@ -1934,7 +1976,8 @@ typedef enum { CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232), /* Set if we should enable TLS false start. */ - CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233), + CURLOPTDEPRECATED(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233, + 8.15.0, "Has no function"), /* Do not squash dot-dot sequences */ CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234), @@ -1954,11 +1997,13 @@ typedef enum { /* Set stream weight, 1 - 256 (default is 16) */ CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), - /* Set stream dependency on another CURL handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + /* Set stream dependency on another curl handle */ + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240, + 8.21.0, "Has no function"), - /* Set E-xclusive stream dependency on another CURL handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + /* Set E-xclusive stream dependency on another curl handle */ + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241, + 8.21.0, "Has no function"), /* Do not send any tftp option requests to the server */ CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), @@ -1982,11 +2027,11 @@ typedef enum { this option is used only if PROXY_SSL_VERIFYPEER is true */ CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), - /* Set if we should verify the proxy in ssl handshake, + /* Set if we should verify the proxy in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), - /* Set if we should verify the Common name from the proxy certificate in ssl + /* Set if we should verify the Common name from the proxy certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches * the provided hostname. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), @@ -1996,13 +2041,16 @@ typedef enum { CURLOPT(CURLOPT_PROXY_SSLVERSION, CURLOPTTYPE_VALUES, 250), /* Set a username for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 251), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, + 251, 8.22.0, "Support was removed"), /* Set a password for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 252), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, + 252, 8.22.0, "Support was removed"), /* Set authentication type for authenticated TLS for proxy */ - CURLOPT(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253), + CURLOPTDEPRECATED(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253, + 8.22.0, "Support was removed"), /* name of the file keeping your private SSL-certificate for proxy */ CURLOPT(CURLOPT_PROXY_SSLCERT, CURLOPTTYPE_STRINGPOINT, 254), @@ -2063,7 +2111,7 @@ typedef enum { /* Head start in milliseconds to give happy eyeballs. */ CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), - /* Function that will be called before a resolver request is made */ + /* Function that is called before a resolver request is made */ CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), /* User data to pass to the resolver start callback. */ @@ -2094,7 +2142,7 @@ typedef enum { /* Specify URL using CURL URL API. */ CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), - /* add trailing data just after no more data is available */ + /* add trailing data after no more data is available */ CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), /* pointer to be passed to HTTP_TRAILER_FUNCTION */ @@ -2172,8 +2220,8 @@ typedef enum { /* used by scp/sftp to verify the host's public key */ CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), - /* Function that will be called immediately before the initial request - is made on a connection (after any protocol negotiation step). */ + /* Function that is called immediately before the initial request + is made on a connection (after any protocol negotiation step). */ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), /* Data passed to the CURLOPT_PREREQFUNCTION callback */ @@ -2223,6 +2271,23 @@ typedef enum { /* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */ CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326), + CURLOPT(CURLOPT_UPLOAD_FLAGS, CURLOPTTYPE_LONG, 327), + + /* set TLS supported signature algorithms */ + CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + + /* RFC 9421 HTTP Message Signatures algorithm */ + CURLOPT(CURLOPT_HTTPSIG_ALGORITHM, CURLOPTTYPE_VALUES, 329), + + /* Hex-encoded key for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_KEY, CURLOPTTYPE_STRINGPOINT, 330), + + /* Key identifier for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_KEYID, CURLOPTTYPE_STRINGPOINT, 331), + + /* Space-separated list of components to sign for HTTP Message Signatures */ + CURLOPT(CURLOPT_HTTPSIG_HEADERS, CURLOPTTYPE_STRINGPOINT, 332), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2248,7 +2313,6 @@ typedef enum { #define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD #define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL -/* */ #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT /* Added in 8.2.0 */ @@ -2259,38 +2323,36 @@ typedef enum { #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ #endif - - /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host - name resolves addresses using more than one IP protocol version, this - option might be handy to force libcurl to use a specific IP version. */ -#define CURL_IPRESOLVE_WHATEVER 0 /* default, uses addresses to all IP +/* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0L /* default, uses addresses to all IP versions that your system allows */ -#define CURL_IPRESOLVE_V4 1 /* uses only IPv4 addresses/connections */ -#define CURL_IPRESOLVE_V6 2 /* uses only IPv6 addresses/connections */ +#define CURL_IPRESOLVE_V4 1L /* uses only IPv4 addresses/connections */ +#define CURL_IPRESOLVE_V6 2L /* uses only IPv6 addresses/connections */ /* Convenient "aliases" */ #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER - /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ -enum { - CURL_HTTP_VERSION_NONE, /* setting this means we do not care, and that we - would like the library to choose the best - possible for us! */ - CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ - CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ - CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ - CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ - CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 - Upgrade */ - CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if - needed. For HTTPS only. For HTTP, this option - makes libcurl return error. */ - CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS - only. For HTTP, this makes libcurl - return error. */ - - CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ -}; +/* These constants are for use with the CURLOPT_HTTP_VERSION option. */ +#define CURL_HTTP_VERSION_NONE 0L /* setting this means we do not care, and + that we would like the library to choose + the best possible for us! */ +#define CURL_HTTP_VERSION_1_0 1L /* please use HTTP 1.0 in the request */ +#define CURL_HTTP_VERSION_1_1 2L /* please use HTTP 1.1 in the request */ +#define CURL_HTTP_VERSION_2_0 3L /* please use HTTP 2 in the request */ +#define CURL_HTTP_VERSION_2TLS 4L /* use version 2 for HTTPS, version 1.1 for + HTTP */ +#define CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE 5L /* please use HTTP 2 without + HTTP/1.1 Upgrade */ +#define CURL_HTTP_VERSION_3 30L /* Use HTTP/3, fallback to HTTP/2 or + HTTP/1 if needed. For HTTPS only. For + HTTP, this option makes libcurl + return error. */ +#define CURL_HTTP_VERSION_3ONLY 31L /* Use HTTP/3 without fallback. For + HTTPS only. For HTTP, this makes + libcurl return error. */ +#define CURL_HTTP_VERSION_LAST 32L /* *ILLEGAL* http version */ /* Convenience definition simple because the name of the version is HTTP/2 and not 2.0. The 2_0 version of the enum name was set while the version was @@ -2300,59 +2362,63 @@ enum { /* * Public API enums for RTSP requests */ -enum { - CURL_RTSPREQ_NONE, /* first in list */ - CURL_RTSPREQ_OPTIONS, - CURL_RTSPREQ_DESCRIBE, - CURL_RTSPREQ_ANNOUNCE, - CURL_RTSPREQ_SETUP, - CURL_RTSPREQ_PLAY, - CURL_RTSPREQ_PAUSE, - CURL_RTSPREQ_TEARDOWN, - CURL_RTSPREQ_GET_PARAMETER, - CURL_RTSPREQ_SET_PARAMETER, - CURL_RTSPREQ_RECORD, - CURL_RTSPREQ_RECEIVE, - CURL_RTSPREQ_LAST /* last in list */ -}; + +#define CURL_RTSPREQ_NONE 0L +#define CURL_RTSPREQ_OPTIONS 1L +#define CURL_RTSPREQ_DESCRIBE 2L +#define CURL_RTSPREQ_ANNOUNCE 3L +#define CURL_RTSPREQ_SETUP 4L +#define CURL_RTSPREQ_PLAY 5L +#define CURL_RTSPREQ_PAUSE 6L +#define CURL_RTSPREQ_TEARDOWN 7L +#define CURL_RTSPREQ_GET_PARAMETER 8L +#define CURL_RTSPREQ_SET_PARAMETER 9L +#define CURL_RTSPREQ_RECORD 10L +#define CURL_RTSPREQ_RECEIVE 11L +#define CURL_RTSPREQ_LAST 12L /* not used */ /* These enums are for use with the CURLOPT_NETRC option. */ +#define CURL_NETRC_IGNORED 0L /* The .netrc is never read. + This is the default. */ +#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred + to one in the .netrc. */ +#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL is ignored. + Unless one is set programmatically, the + .netrc is queried. */ enum CURL_NETRC_OPTION { - CURL_NETRC_IGNORED, /* The .netrc will never be read. - * This is the default. */ - CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred - * to one in the .netrc. */ - CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. - * Unless one is set programmatically, the .netrc - * will be queried. */ - CURL_NETRC_LAST + /* we set a single member here, to make sure we still provide the enum, but + the values to use are defined above with L suffixes */ + CURL_NETRC_LAST = 3 }; -#define CURL_SSLVERSION_DEFAULT 0 -#define CURL_SSLVERSION_TLSv1 1 /* TLS 1.x */ -#define CURL_SSLVERSION_SSLv2 2 -#define CURL_SSLVERSION_SSLv3 3 -#define CURL_SSLVERSION_TLSv1_0 4 -#define CURL_SSLVERSION_TLSv1_1 5 -#define CURL_SSLVERSION_TLSv1_2 6 -#define CURL_SSLVERSION_TLSv1_3 7 +#define CURL_SSLVERSION_DEFAULT 0L +#define CURL_SSLVERSION_TLSv1 1L /* TLS 1.x */ +#define CURL_SSLVERSION_SSLv2 2L +#define CURL_SSLVERSION_SSLv3 3L +#define CURL_SSLVERSION_TLSv1_0 4L +#define CURL_SSLVERSION_TLSv1_1 5L +#define CURL_SSLVERSION_TLSv1_2 6L +#define CURL_SSLVERSION_TLSv1_3 7L -#define CURL_SSLVERSION_LAST 8 /* never use, keep last */ +#define CURL_SSLVERSION_LAST 8L /* never use, keep last */ -#define CURL_SSLVERSION_MAX_NONE 0 +#define CURL_SSLVERSION_MAX_NONE 0L #define CURL_SSLVERSION_MAX_DEFAULT (CURL_SSLVERSION_TLSv1 << 16) #define CURL_SSLVERSION_MAX_TLSv1_0 (CURL_SSLVERSION_TLSv1_0 << 16) #define CURL_SSLVERSION_MAX_TLSv1_1 (CURL_SSLVERSION_TLSv1_1 << 16) #define CURL_SSLVERSION_MAX_TLSv1_2 (CURL_SSLVERSION_TLSv1_2 << 16) #define CURL_SSLVERSION_MAX_TLSv1_3 (CURL_SSLVERSION_TLSv1_3 << 16) - /* never use, keep last */ +/* never use, keep last */ #define CURL_SSLVERSION_MAX_LAST (CURL_SSLVERSION_LAST << 16) +#define CURL_TLSAUTH_NONE 0L +#define CURL_TLSAUTH_SRP 1L + enum CURL_TLSAUTH { - CURL_TLSAUTH_NONE, - CURL_TLSAUTH_SRP, - CURL_TLSAUTH_LAST /* never use, keep last */ + /* we set a single member here, to make sure we still provide the enum, + but the values to use are defined above with L suffixes */ + CURL_TLSAUTH_LAST = 2 }; /* symbols to use with CURLOPT_POSTREDIR. @@ -2360,25 +2426,27 @@ enum CURL_TLSAUTH { can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ -#define CURL_REDIR_GET_ALL 0 -#define CURL_REDIR_POST_301 1 -#define CURL_REDIR_POST_302 2 -#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_GET_ALL 0L +#define CURL_REDIR_POST_301 1L +#define CURL_REDIR_POST_302 2L +#define CURL_REDIR_POST_303 4L #define CURL_REDIR_POST_ALL \ - (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) - -typedef enum { - CURL_TIMECOND_NONE, + (CURL_REDIR_POST_301 | CURL_REDIR_POST_302 | CURL_REDIR_POST_303) - CURL_TIMECOND_IFMODSINCE, - CURL_TIMECOND_IFUNMODSINCE, - CURL_TIMECOND_LASTMOD, +#define CURL_TIMECOND_NONE 0L +#define CURL_TIMECOND_IFMODSINCE 1L +#define CURL_TIMECOND_IFUNMODSINCE 2L +#define CURL_TIMECOND_LASTMOD 3L - CURL_TIMECOND_LAST +typedef enum { + /* we set a single member here, to make sure we still provide + the enum typedef, but the values to use are defined above with L + suffixes */ + CURL_TIMECOND_LAST = 4 } curl_TimeCond; /* Special size_t value signaling a null-terminated string. */ -#define CURL_ZERO_TERMINATED ((size_t) -1) +#define CURL_ZERO_TERMINATED ((size_t)-1) /* curl_strequal() and curl_strnequal() are subject for removal in a future release */ @@ -2390,7 +2458,7 @@ typedef struct curl_mime curl_mime; /* Mime context. */ typedef struct curl_mimepart curl_mimepart; /* Mime part context. */ /* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */ -#define CURLMIMEOPT_FORMESCAPE (1<<0) /* Use backslash-escaping for forms. */ +#define CURLMIMEOPT_FORMESCAPE (1L << 0) /* Use backslash-escaping for forms */ /* * NAME curl_mime_init() @@ -2597,7 +2665,7 @@ curl_formadd(struct curl_httppost **httppost, /* * callback function for curl_formget() - * The void *arg pointer will be the one passed as second argument to + * The void *arg pointer is the one passed as second argument to * curl_formget(). * The character buffer passed to it must not be freed. * Should return the buffer length passed to it as the argument "len" on @@ -2612,7 +2680,7 @@ typedef size_t (*curl_formget_callback)(void *arg, const char *buf, * DESCRIPTION * * Serialize a curl_httppost struct built with curl_formadd(). - * Accepts a void pointer as second argument which will be passed to + * Accepts a void pointer as second argument which is passed to * the curl_formget_callback function. * Returns 0 on success. */ @@ -2657,7 +2725,7 @@ CURL_EXTERN char *curl_version(void); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -CURL_EXTERN char *curl_easy_escape(CURL *handle, +CURL_EXTERN char *curl_easy_escape(CURL *curl, const char *string, int length); @@ -2665,21 +2733,20 @@ CURL_EXTERN char *curl_easy_escape(CURL *handle, CURL_EXTERN char *curl_escape(const char *string, int length); - /* * NAME curl_easy_unescape() * * DESCRIPTION * - * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * Unescapes URL encoding in strings (converts all %XX codes to their 8-bit * versions). This function returns a new allocated string or NULL if an error * occurred. * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -CURL_EXTERN char *curl_easy_unescape(CURL *handle, +CURL_EXTERN char *curl_easy_unescape(CURL *curl, const char *string, - int length, + int inlength, int *outlength); /* the previous version */ @@ -2703,10 +2770,9 @@ CURL_EXTERN void curl_free(void *p); * * curl_global_init() should be invoked exactly once for each application that * uses libcurl and before any call of other libcurl functions. - + * * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the * curl_version_info_data.features flag (fetch by curl_version_info()). - */ CURL_EXTERN CURLcode curl_global_init(long flags); @@ -2719,8 +2785,8 @@ CURL_EXTERN CURLcode curl_global_init(long flags); * for each application that uses libcurl. This function can be used to * initialize libcurl and set user defined memory management callback * functions. Users can implement memory management routines to check for - * memory leaks, check for mis-use of the curl library etc. User registered - * callback routines will be invoked by this library instead of the system + * memory leaks, check for misuse of the curl library etc. User registered + * callback routines is invoked by this library instead of the system * memory management routines like malloc, free etc. */ CURL_EXTERN CURLcode curl_global_init_mem(long flags, @@ -2747,10 +2813,9 @@ CURL_EXTERN void curl_global_cleanup(void); * * curl_global_trace() can be invoked at application start to * configure which components in curl should participate in tracing. - + * * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the * curl_version_info_data.features flag (fetch by curl_version_info()). - */ CURL_EXTERN CURLcode curl_global_trace(const char *config); @@ -2770,20 +2835,20 @@ struct curl_slist { * *before* curl_global_init(). * * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The - * backend can also be specified via the name parameter (passing -1 as id). - * If both id and name are specified, the name will be ignored. If neither id - * nor name are specified, the function will fail with - * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the - * NULL-terminated list of available backends. + * backend can also be specified via the name parameter (passing -1 as id). If + * both id and name are specified, the name is ignored. If neither id nor + * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND + * and set the "avail" pointer to the null-terminated list of available + * backends. * * Upon success, the function returns CURLSSLSET_OK. * * If the specified SSL backend is not available, the function returns - * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated - * list of available SSL backends. + * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a + * null-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a - * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. */ struct curl_ssl_backend { @@ -2807,8 +2872,8 @@ CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, * * DESCRIPTION * - * Appends a string to a linked list. If no list exists, it will be created - * first. Returns the new list, after appending. + * Appends a string to a linked list. If no list exists, it is created first. + * Returns the new list, after appending. */ CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data); @@ -2953,7 +3018,11 @@ typedef enum { CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65, CURLINFO_USED_PROXY = CURLINFO_LONG + 66, CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67, - CURLINFO_LASTONE = 67 + CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, + CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69, + CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70, + CURLINFO_SIZE_DELIVERED = CURLINFO_OFF_T + 71, + CURLINFO_LASTONE = 71 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -2972,13 +3041,13 @@ typedef enum { CURLCLOSEPOLICY_LAST /* last, never use this */ } curl_closepolicy; -#define CURL_GLOBAL_SSL (1<<0) /* no purpose since 7.57.0 */ -#define CURL_GLOBAL_WIN32 (1<<1) -#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1 << 0) /* no purpose since 7.57.0 */ +#define CURL_GLOBAL_WINSOCK (1 << 1) +#define CURL_GLOBAL_WIN32 CURL_GLOBAL_WINSOCK /* for compatibility */ +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL | CURL_GLOBAL_WINSOCK) #define CURL_GLOBAL_NOTHING 0 #define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL -#define CURL_GLOBAL_ACK_EINTR (1<<2) - +#define CURL_GLOBAL_ACK_EINTR (1 << 2) /***************************************************************************** * Setup defines, protos etc for the sharing stuff. @@ -2987,9 +3056,8 @@ typedef enum { /* Different data locks for a single share */ typedef enum { CURL_LOCK_DATA_NONE = 0, - /* CURL_LOCK_DATA_SHARE is used internally to say that - * the locking is just made to change the internal state of the share - * itself. + /* CURL_LOCK_DATA_SHARE is used internally to say that the locking is made + * to change the internal state of the share itself. */ CURL_LOCK_DATA_SHARE, CURL_LOCK_DATA_COOKIE, @@ -3017,7 +3085,6 @@ typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr); - typedef enum { CURLSHE_OK, /* all is fine */ CURLSHE_BAD_OPTION, /* 1 */ @@ -3040,9 +3107,8 @@ typedef enum { } CURLSHoption; CURL_EXTERN CURLSH *curl_share_init(void); -CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, - ...); -CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *sh); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -3064,11 +3130,10 @@ typedef enum { CURLVERSION_LAST /* never actually use this */ } CURLversion; -/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by - basically all programs ever that want to get version information. It is - meant to be a built-in version number for what kind of struct the caller - expects. If the struct ever changes, we redefine the NOW to another enum - from above. */ +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by programs + that want to get version information. It is meant to be a built-in + version number for what kind of struct the caller expects. If the struct + ever changes, we redefine the NOW to another enum from above. */ #define CURLVERSION_NOW CURLVERSION_TWELFTH struct curl_version_info_data { @@ -3151,7 +3216,8 @@ typedef struct curl_version_info_data curl_version_info_data; supported */ #define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ #define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported + (deprecated) */ #define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ #define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper is supported */ @@ -3180,7 +3246,7 @@ typedef struct curl_version_info_data curl_version_info_data; * This function returns a pointer to a static copy of the version info * struct. See above. */ -CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion stamp); /* * NAME curl_easy_strerror() @@ -3191,7 +3257,7 @@ CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_easy_strerror(CURLcode); +CURL_EXTERN const char *curl_easy_strerror(CURLcode error); /* * NAME curl_share_strerror() @@ -3202,7 +3268,7 @@ CURL_EXTERN const char *curl_easy_strerror(CURLcode); * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ -CURL_EXTERN const char *curl_share_strerror(CURLSHcode); +CURL_EXTERN const char *curl_share_strerror(CURLSHcode error); /* * NAME curl_easy_pause() @@ -3210,50 +3276,98 @@ CURL_EXTERN const char *curl_share_strerror(CURLSHcode); * DESCRIPTION * * The curl_easy_pause function pauses or unpauses transfers. Select the new - * state by setting the bitmask, use the convenience defines below. + * state by setting the action bitmask, use the convenience defines below. * */ -CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); +CURL_EXTERN CURLcode curl_easy_pause(CURL *curl, int action); -#define CURLPAUSE_RECV (1<<0) -#define CURLPAUSE_RECV_CONT (0) +#define CURLPAUSE_RECV (1 << 0) +#define CURLPAUSE_RECV_CONT 0 -#define CURLPAUSE_SEND (1<<2) -#define CURLPAUSE_SEND_CONT (0) +#define CURLPAUSE_SEND (1 << 2) +#define CURLPAUSE_SEND_CONT 0 -#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) -#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) +#define CURLPAUSE_ALL (CURLPAUSE_RECV | CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT) + +/* + * NAME curl_easy_ssls_import() + * + * DESCRIPTION + * + * The curl_easy_ssls_import function adds a previously exported SSL session + * to the SSL session cache of the easy handle (or the underlying share). + */ +CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *curl, + const char *session_key, + const unsigned char *shmac, + size_t shmac_len, + const unsigned char *sdata, + size_t sdata_len); + +/* This is the curl_ssls_export_cb callback prototype. It + * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */ +typedef CURLcode curl_ssls_export_cb(CURL *curl, + void *userptr, + const char *session_key, + const unsigned char *shmac, + size_t shmac_len, + const unsigned char *sdata, + size_t sdata_len, + curl_off_t valid_until, + int ietf_tls_id, + const char *alpn, + size_t earlydata_max); -#ifdef __cplusplus +/* + * NAME curl_easy_ssls_export() + * + * DESCRIPTION + * + * The curl_easy_ssls_export function iterates over all SSL sessions stored + * in the easy handle (or underlying share) and invokes the passed + * callback. + * + */ +CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *curl, + curl_ssls_export_cb *export_fn, + void *userptr); + +#ifdef __cplusplus } /* end of extern "C" */ #endif /* unfortunately, the easy.h and multi.h include files need options and info - stuff before they can be included! */ + stuff before they can be included! */ #include "easy.h" /* nothing in curl is fun without the easy stuff */ #include "multi.h" #include "urlapi.h" #include "options.h" #include "header.h" #include "websockets.h" -#ifndef CURL_SKIP_INCLUDE_MPRINTF #include "mprintf.h" -#endif /* the typechecker does not work in C++ (yet) */ -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ - ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ +#if ((defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || \ + (defined(__clang__) && __clang_major__ >= 14)) && \ !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) #include "typecheck-gcc.h" #else #if defined(__STDC__) && (__STDC__ >= 1) -/* This preprocessor magic that replaces a call with the exact same call is - only done to make sure application authors pass exactly three arguments - to these functions. */ -#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) -#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) -#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +/* This preprocessor magic ensures that application authors pass exactly three + arguments to these functions. For compatibility with C++ global namespace + '::' and reusing these symbols as method names, while also avoiding + recursive macros, use a two-stage solution. */ +#define curl_exactly_three_arguments(a, b, c) (a, b, c) +#define curl_easy_setopt(handle, opt, param) \ + curl_easy_setopt curl_exactly_three_arguments(handle, opt, param) +#define curl_easy_getinfo(handle, info, arg) \ + curl_easy_getinfo curl_exactly_three_arguments(handle, info, arg) +#define curl_share_setopt(share, opt, param) \ + curl_share_setopt curl_exactly_three_arguments(share, opt, param) +#define curl_multi_setopt(handle, opt, param) \ + curl_multi_setopt curl_exactly_three_arguments(handle, opt, param) #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */ diff --git a/curl/curlver.h b/curl/curlver.h index 673032f..b44fa92 100644 --- a/curl/curlver.h +++ b/curl/curlver.h @@ -32,23 +32,22 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.10.1-DEV" +#define LIBCURL_VERSION "8.23.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 10 -#define LIBCURL_VERSION_PATCH 1 - +#define LIBCURL_VERSION_MINOR 23 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier - parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will - always follow this syntax: + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always + follows this syntax: 0xXXYYZZ Where XX, YY and ZZ are the main version, release and patch numbers in hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + using two digits. Version 1.2 would appear as "0x010200" while 9.11.7 appears as "0x090b07". This 6-digit (24 bits) hexadecimal number does not show pre-release number, @@ -57,9 +56,8 @@ Note: This define is the full hex number and _does not_ use the CURL_VERSION_BITS() macro since curl's own configure script greps for it - and needs it to contain the full number. -*/ -#define LIBCURL_VERSION_NUM 0x080a01 + and needs it to contain the full number. */ +#define LIBCURL_VERSION_NUM 0x081700 /* * This is the date and time when the full source package was created. The @@ -70,10 +68,10 @@ * * "2007-11-23" */ -#define LIBCURL_TIMESTAMP "[unreleased]" +#define LIBCURL_TIMESTAMP "2026-10-14" -#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) -#define CURL_AT_LEAST_VERSION(x,y,z) \ +#define CURL_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z)) +#define CURL_AT_LEAST_VERSION(x, y, z) \ (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) #endif /* CURLINC_CURLVER_H */ diff --git a/curl/easy.h b/curl/easy.h index 71b8dd4..197e6e7 100644 --- a/curl/easy.h +++ b/curl/easy.h @@ -23,7 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -50,15 +50,14 @@ CURL_EXTERN void curl_easy_cleanup(CURL *curl); * * Request internal information from the curl session with this function. * The third argument MUST be pointing to the specific type of the used option - * which is documented in each manpage of the option. The data pointed to - * will be filled in accordingly and can be relied upon only if the function + * which is documented in each man page of the option. The data pointed to + * is filled in accordingly and can be relied upon only if the function * returns CURLE_OK. This function is intended to get used *AFTER* a performed * transfer, all results from this function are undefined until the transfer * is completed. */ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); - /* * NAME curl_easy_duphandle() * @@ -67,7 +66,7 @@ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); * Creates a new curl session handle with the same options set for the handle * passed in. Duplicating a handle could only be a matter of cloning data and * options, internal state info and things like persistent connections cannot - * be transferred. It is useful in multithreaded applications when you can run + * be transferred. It is useful in multi-threaded applications when you can run * curl_easy_duphandle() for each new thread to avoid a series of identical * curl_easy_setopt() invokes in every thread. */ @@ -78,8 +77,8 @@ CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); * * DESCRIPTION * - * Re-initializes a CURL handle to the default values. This puts back the - * handle to the same state as it was in when it was just created. + * Re-initializes a curl handle to the default values. This puts back the + * handle to the same state as it was in when it was created. * * It does keep: live connections, the Session ID cache, the DNS cache and the * cookies. @@ -108,7 +107,6 @@ CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n); - /* * NAME curl_easy_upkeep() * @@ -118,7 +116,7 @@ CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, */ CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); -#ifdef __cplusplus +#ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/curl/header.h b/curl/header.h index 8df11e1..5c3281a 100644 --- a/curl/header.h +++ b/curl/header.h @@ -24,25 +24,25 @@ * ***************************************************************************/ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif struct curl_header { char *name; /* this might not use the same case */ char *value; - size_t amount; /* number of headers using this name */ + size_t amount; /* number of headers using this name */ size_t index; /* ... of this instance, 0 or higher */ unsigned int origin; /* see bits below */ void *anchor; /* handle privately used by libcurl */ }; /* 'origin' bits */ -#define CURLH_HEADER (1<<0) /* plain server header */ -#define CURLH_TRAILER (1<<1) /* trailers */ -#define CURLH_CONNECT (1<<2) /* CONNECT headers */ -#define CURLH_1XX (1<<3) /* 1xx headers */ -#define CURLH_PSEUDO (1<<4) /* pseudo headers */ +#define CURLH_HEADER (1 << 0) /* plain server header */ +#define CURLH_TRAILER (1 << 1) /* trailers */ +#define CURLH_CONNECT (1 << 2) /* CONNECT headers */ +#define CURLH_1XX (1 << 3) /* 1xx headers */ +#define CURLH_PSEUDO (1 << 4) /* pseudo headers */ typedef enum { CURLHE_OK, @@ -55,14 +55,14 @@ typedef enum { CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ } CURLHcode; -CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, +CURL_EXTERN CURLHcode curl_easy_header(CURL *curl, const char *name, - size_t index, + size_t nameindex, unsigned int origin, int request, struct curl_header **hout); -CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *curl, unsigned int origin, int request, struct curl_header *prev); diff --git a/curl/libcurl.lib b/curl/libcurl.lib index 26ad600..d33e65e 100644 Binary files a/curl/libcurl.lib and b/curl/libcurl.lib differ diff --git a/curl/mprintf.h b/curl/mprintf.h index 88059c8..9272e74 100644 --- a/curl/mprintf.h +++ b/curl/mprintf.h @@ -28,7 +28,7 @@ #include /* needed for FILE */ #include "curl.h" /* for CURL_EXTERN */ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -38,7 +38,7 @@ extern "C" { defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(CURL_NO_FMT_CHECKS) #if defined(__MINGW32__) && !defined(__clang__) -#if defined(__MINGW_PRINTF_FORMAT) /* mingw-w64 3.0.0+. Needs stdio.h. */ +#ifdef __MINGW_PRINTF_FORMAT /* mingw-w64 3.0.0+. Needs stdio.h. */ #define CURL_TEMP_PRINTF(fmt, arg) \ __attribute__((format(__MINGW_PRINTF_FORMAT, fmt, arg))) #else @@ -78,7 +78,7 @@ CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args) #undef CURL_TEMP_PRINTF -#ifdef __cplusplus +#ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/curl/multi.h b/curl/multi.h index 7b6c351..6a79e0a 100644 --- a/curl/multi.h +++ b/curl/multi.h @@ -24,20 +24,20 @@ * ***************************************************************************/ /* - This is an "external" header file. Do not give away any internals here! - - GOALS - - o Enable a "pull" interface. The application that uses libcurl decides where - and when to ask libcurl to get/send data. - - o Enable multiple simultaneous transfers in the same thread without making it - complicated for the application. - - o Enable the application to select() on its own file descriptors and curl's - file descriptors simultaneous easily. - -*/ + * This is an "external" header file. Do not give away any internals here! + * + * GOALS + * + * o Enable a "pull" interface. The application that uses libcurl decides where + * and when to ask libcurl to get/send data. + * + * o Enable multiple simultaneous transfers in the same thread without making + * it complicated for the application. + * + * o Enable the application to select() on its own file descriptors and curl's + * file descriptors simultaneous easily. + * + */ /* * This header file should not really need to include "curl.h" since curl.h @@ -50,15 +50,11 @@ */ #include "curl.h" -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif -#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) -typedef struct Curl_multi CURLM; -#else typedef void CURLM; -#endif typedef enum { CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or @@ -81,9 +77,8 @@ typedef enum { CURLM_LAST } CURLMcode; -/* just to make code nicer when using curl_multi_socket() you can now check - for CURLM_CALL_MULTI_SOCKET too in the same style it works for - curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +/* You can check for CURLM_CALL_MULTI_SOCKET too in the same style it works + for curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM /* bitmask bits for CURLMOPT_PIPELINING */ @@ -137,29 +132,29 @@ CURL_EXTERN CURLM *curl_multi_init(void); * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, - CURL *curl_handle); - - /* - * Name: curl_multi_remove_handle() - * - * Desc: removes a curl handle from the multi stack again - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, - CURL *curl_handle); - - /* - * Name: curl_multi_fdset() - * - * Desc: Ask curl for its fd_set sets. The app can use these to select() or - * poll() on. We want curl_multi_perform() called as soon as one of - * them are ready. - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *m, + CURL *curl); + +/* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *m, + CURL *curl); + +/* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, @@ -173,7 +168,7 @@ CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -187,7 +182,7 @@ CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -200,68 +195,68 @@ CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); - - /* - * Name: curl_multi_perform() - * - * Desc: When the app thinks there is data available for curl it calls this - * function to read/write whatever there is right now. This returns - * as soon as the reads and writes are done. This function does not - * require that there actually is data available for reading or that - * data can be written, it can be called just in case. It returns - * the number of handles that still transfer data in the second - * argument's integer-pointer. - * - * Returns: CURLMcode type, general multi error code. *NOTE* that this only - * returns errors etc regarding the whole multi stack. There might - * still have occurred problems on individual transfers even when - * this returns OK. - */ -CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *m); + +/* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there is data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called. It returns the number of + * handles that still transfer data in the second argument's + * integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on individual transfers even when + * this returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *m, int *running_handles); - /* - * Name: curl_multi_cleanup() - * - * Desc: Cleans up and removes a whole multi stack. It does not free or - * touch any individual easy handles in any way. We need to define - * in what state those handles will be if this function is called - * in the middle of a transfer. - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); +/* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles are going to be if this function is + * called in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *m); /* * Name: curl_multi_info_read() * * Desc: Ask the multi handle if there is any messages/informationals from * the individual transfers. Messages include informationals such as - * error code from the transfer or just the fact that a transfer is + * error code from the transfer or the fact that a transfer is * completed. More details on these should be written down as well. * - * Repeated calls to this function will return a new struct each - * time, until a special "end of msgs" struct is returned as a signal - * that there is no more to get at this point. + * Repeated calls to this function return a new struct each time, + * until a special "end of msgs" struct is returned as a signal that + * there is no more to get at this point. * - * The data the returned pointer points to will not survive calling + * The data the returned pointer points to does not survive calling * curl_multi_cleanup(). * - * The 'CURLMsg' struct is meant to be very simple and only contain - * very basic information. If more involved information is wanted, - * we will provide the particular "transfer handle" in that struct - * and that should/could/would be used in subsequent - * curl_easy_getinfo() calls (or similar). The point being that we - * must never expose complex structs to applications, as then we will - * undoubtably get backwards compatibility problems in the future. + * The 'CURLMsg' struct is meant to be simple and only contain basic + * information. If more involved information is wanted, we provide + * the particular "transfer handle" in that struct and that + * should/could/would be used in subsequent curl_easy_getinfo() calls + * (or similar). The point being that we must never expose complex + * structs to applications, as then we undoubtably get backwards + * compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out * of structs. It also writes the number of messages left in the * queue (after this read) in the integer the second argument points * to. */ -CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue); /* @@ -273,7 +268,7 @@ CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, * * Returns: A pointer to a null-terminated error message. */ -CURL_EXTERN const char *curl_multi_strerror(CURLMcode); +CURL_EXTERN const char *curl_multi_strerror(CURLMcode error); /* * Name: curl_multi_socket() and @@ -282,7 +277,7 @@ CURL_EXTERN const char *curl_multi_strerror(CURLMcode); * Desc: An alternative version of curl_multi_perform() that allows the * application to pass in one of the file descriptors that have been * detected to have "action" on them and let libcurl perform. - * See manpage for details. + * See man page for details. */ #define CURL_POLL_NONE 0 #define CURL_POLL_IN 1 @@ -313,27 +308,27 @@ typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ * * Returns: The callback should return zero. */ -typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ +typedef int (*curl_multi_timer_callback)(CURLM *m, /* multi handle */ long timeout_ms, /* see above */ void *userp); /* private callback pointer */ CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); +curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles); -CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles); CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +curl_multi_socket_all(CURLM *m, int *running_handles); #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* This macro below was added in 7.16.3 to push users who recompile to use - the new curl_multi_socket_action() instead of the old curl_multi_socket() -*/ -#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) + * the new curl_multi_socket_action() instead of the old curl_multi_socket() + */ +#define curl_multi_socket(x, y, z) curl_multi_socket_action(x, y, 0, z) #endif /* @@ -345,8 +340,8 @@ curl_multi_socket_all(CURLM *multi_handle, int *running_handles); * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *milliseconds); +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *m, + long *timeout_ms); typedef enum { /* This is the socket callback function pointer */ @@ -355,10 +350,10 @@ typedef enum { /* This is the argument passed to the socket callback */ CURLOPT(CURLMOPT_SOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 2), - /* set to 1 to enable pipelining for this multi handle */ + /* set to 1 to enable pipelining for this multi handle */ CURLOPT(CURLMOPT_PIPELINING, CURLOPTTYPE_LONG, 3), - /* This is the timer callback function pointer */ + /* This is the timer callback function pointer */ CURLOPT(CURLMOPT_TIMERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 4), /* This is the argument passed to the timer callback */ @@ -374,11 +369,11 @@ typedef enum { CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), /* a connection with a content-length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), /* a connection with a chunk length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), /* a list of site names(+port) that are blocked from pipelining */ @@ -399,9 +394,39 @@ typedef enum { /* maximum number of concurrent streams to support on a connection */ CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16), + /* network has changed, adjust caches/connection reuse */ + CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17), + + /* This is the notify callback function pointer */ + CURLOPT(CURLMOPT_NOTIFYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 18), + + /* This is the argument passed to the notify callback */ + CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19), + + /* maximum number of threads used with threaded DNS resolver */ + CURLOPT(CURLMOPT_RESOLVE_THREADS_MAX, CURLOPTTYPE_LONG, 20), + + /* set to 1L for not joining threads when multi is cleaned up */ + CURLOPT(CURLMOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 21), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; +/* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */ + +/* - CURLMNWC_CLEAR_ALL tells libcurl to clear "everything" that could be + associated with this network, including both connections and DNS data. */ +#define CURLMNWC_CLEAR_ALL (1L << 0) + +/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing + connections. Connections that are idle are closed. Ongoing transfers do + continue with the connection they have. */ +#define CURLMNWC_CLEAR_CONNS (1L << 1) + +/* - CURLMNWC_CLEAR_DNS tells libcurl to clear the DNS cache associated with + this multi handle. Ongoing transfers keep using their already resolved + addresses, but future name resolutions are performed again. */ +#define CURLMNWC_CLEAR_DNS (1L << 2) /* * Name: curl_multi_setopt() @@ -410,10 +435,9 @@ typedef enum { * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...); - /* * Name: curl_multi_assign() * @@ -423,7 +447,7 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp); /* @@ -436,7 +460,38 @@ CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, * * Returns: NULL on failure, otherwise a CURL **array pointer */ -CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle); +CURL_EXTERN CURL **curl_multi_get_handles(CURLM *m); + +typedef enum { + CURLMINFO_NONE, /* first, never use this */ + /* The number of easy handles currently managed by the multi handle, + * e.g. have been added but not yet removed. */ + CURLMINFO_XFERS_CURRENT = 1, + /* The number of easy handles running, e.g. not done and not queueing. */ + CURLMINFO_XFERS_RUNNING = 2, + /* The number of easy handles waiting to start, e.g. for a connection + * to become available due to limits on parallelism, max connections + * or other factors. */ + CURLMINFO_XFERS_PENDING = 3, + /* The number of easy handles finished, waiting for their results to + * be read via `curl_multi_info_read()`. */ + CURLMINFO_XFERS_DONE = 4, + /* The total number of easy handles added to the multi handle, ever. */ + CURLMINFO_XFERS_ADDED = 5, + + CURLMINFO_LASTENTRY /* the last unused */ +} CURLMinfo_offt; + +/* + * Name: curl_multi_get_offt() + * + * Desc: Retrieves a numeric value for the `CURLMINFO_*` enums. + * + * Returns: CULRM_OK or error when value could not be obtained. + */ +CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *m, + CURLMinfo_offt info, + curl_off_t *pvalue); /* * Name: curl_push_callback @@ -469,15 +524,36 @@ typedef int (*curl_push_callback)(CURL *parent, * * Desc: Ask curl for fds for polling. The app can use these to poll on. * We want curl_multi_perform() called as soon as one of them are - * ready. Passing zero size allows to get just a number of fds. + * ready. Passing zero size allows to get a number of fds. * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *m, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count); +/* + * Notifications dispatched by a multi handle, when enabled. + */ +#define CURLMNOTIFY_INFO_READ 0 +#define CURLMNOTIFY_EASY_DONE 1 +#define CURLMNOTIFY_LAST 2 /* last, not used */ + +/* + * Callback to install via CURLMOPT_NOTIFYFUNCTION. + */ +typedef void (*curl_notify_callback)(CURLM *m, + unsigned int notification, + CURL *easy, + void *user_data); + +CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *m, + unsigned int notification); + +CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *m, + unsigned int notification); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/curl/options.h b/curl/options.h index 1ed76a9..835a722 100644 --- a/curl/options.h +++ b/curl/options.h @@ -24,7 +24,7 @@ * ***************************************************************************/ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -44,7 +44,7 @@ typedef enum { /* "alias" means it is provided for old programs to remain functional, we prefer another name */ -#define CURLOT_FLAG_ALIAS (1<<0) +#define CURLOT_FLAG_ALIAS (1 << 0) /* The CURLOPTTYPE_* id ranges can still be used to figure out what type/size to use for curl_easy_setopt() for the given id */ diff --git a/curl/system.h b/curl/system.h index e5be256..30216ea 100644 --- a/curl/system.h +++ b/curl/system.h @@ -36,78 +36,37 @@ * curl_off_t * ---------- * - * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * For any given platform/compiler curl_off_t MUST be typedef'ed to a 64-bit * wide signed integral data type. The width of this data type must remain * constant and independent of any possible large file support settings. * - * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit - * wide signed integral data type if there is no 64-bit type. - * * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall * only be violated if off_t is the only 64-bit data type available and the * size of off_t is independent of large file support settings. Keep your * build on the safe side avoiding an off_t gating. If you have a 64-bit * off_t then take for sure that another 64-bit data type exists, dig deeper - * and you will find it. + * to find it. * */ -#if defined(__DJGPP__) || defined(__GO32__) -# if defined(__DJGPP__) && (__DJGPP__ > 1) -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T int - -#elif defined(__SALFORDC__) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +#ifdef __DJGPP__ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__BORLANDC__) -# if (__BORLANDC__ < 0x520) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else -# define CURL_TYPEOF_CURL_OFF_T __int64 -# define CURL_FORMAT_CURL_OFF_T "I64d" -# define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# endif -# define CURL_TYPEOF_CURL_SOCKLEN_T int - -#elif defined(__TURBOC__) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__POCC__) -# if (__POCC__ < 280) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# elif defined(_MSC_VER) +# ifdef _MSC_VER # define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" @@ -123,7 +82,7 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__LCC__) -# if defined(__MCST__) /* MCST eLbrus Compiler Collection */ +# ifdef __MCST__ /* MCST eLbrus Compiler Collection */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -159,17 +118,23 @@ # define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int #elif defined(__TANDEM) -# if ! defined(__LP64) - /* Required for 32-bit NonStop builds only. */ -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# define CURL_TYPEOF_CURL_SOCKLEN_T int -# endif +# ifndef __LP64 +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int +# endif -#elif defined(_WIN32_WCE) +#elif defined(UNDER_CE) # define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" @@ -188,7 +153,7 @@ # define CURL_PULL_SYS_TYPES_H 1 #elif defined(__VMS) -# if defined(__VAX) +# ifdef __VAX # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -214,19 +179,13 @@ # define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__MVS__) -# if defined(_LONG_LONG) +# ifdef _LONG_LONG # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else +# else /* _LP64 and default */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -239,22 +198,13 @@ #elif defined(__370__) # if defined(__IBMC__) || defined(__IBMCPP__) -# if defined(_ILP32) -# elif defined(_LP64) -# endif -# if defined(_LONG_LONG) +# ifdef _LONG_LONG # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL -# elif defined(_LP64) -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# else +# else /* _LP64 and default */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" @@ -307,7 +257,7 @@ # define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__xlc__) /* IBM xlc compiler */ -# if !defined(_LP64) +# ifndef _LP64 # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" @@ -325,7 +275,7 @@ # define CURL_PULL_SYS_SOCKET_H 1 #elif defined(__hpux) /* HP aCC compiler */ -# if !defined(_LP64) +# ifndef _LP64 # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" @@ -347,26 +297,17 @@ /* ===================================== */ #elif defined(_MSC_VER) -# if (_MSC_VER >= 1800) +# if _MSC_VER >= 1800 # include -# define CURL_TYPEOF_CURL_OFF_T __int64 # define CURL_FORMAT_CURL_OFF_T PRId64 # define CURL_FORMAT_CURL_OFF_TU PRIu64 -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define CURL_TYPEOF_CURL_OFF_T __int64 +# else # define CURL_FORMAT_CURL_OFF_T "I64d" # define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# else -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL # endif +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 # define CURL_TYPEOF_CURL_SOCKLEN_T int /* ===================================== */ @@ -379,13 +320,18 @@ defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ defined(__XTENSA__) || \ - (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" # define CURL_FORMAT_CURL_OFF_TU "llu" # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL +# if (__GNUC__ >= 4) || \ + ((__GNUC__ == 3) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 4)) +# define CURL_POPCOUNT64(x) __builtin_popcountll(x) +# define CURL_CTZ64(x) __builtin_ctzll(x) +# endif # elif defined(__LP64__) || \ defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ defined(__e2k__) || \ @@ -396,6 +342,11 @@ # define CURL_FORMAT_CURL_OFF_TU "lu" # define CURL_SUFFIX_CURL_OFF_T L # define CURL_SUFFIX_CURL_OFF_TU UL +# if (__GNUC__ >= 4) || \ + ((__GNUC__ == 3) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 4)) +# define CURL_POPCOUNT64(x) __builtin_popcountl(x) +# define CURL_CTZ64(x) __builtin_ctzl(x) +# endif # endif # define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t # define CURL_PULL_SYS_TYPES_H 1 @@ -403,12 +354,12 @@ #else /* generic "safe guess" on old 32-bit style */ -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL -# define CURL_TYPEOF_CURL_SOCKLEN_T int +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int #endif #ifdef _AIX @@ -416,20 +367,20 @@ #define CURL_PULL_SYS_POLL_H #endif -/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ -/* sys/types.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file + sys/types.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_TYPES_H # include #endif -/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ -/* sys/socket.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file + sys/socket.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_SOCKET_H # include #endif -/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ -/* sys/poll.h is required here to properly make type definitions below. */ +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file + sys/poll.h is required here to properly make type definitions below. */ #ifdef CURL_PULL_SYS_POLL_H # include #endif @@ -445,52 +396,4 @@ typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; #endif -/* - * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow - * these to be visible and exported by the external libcurl interface API, - * while also making them visible to the library internals, simply including - * curl_setup.h, without actually needing to include curl.h internally. - * If some day this section would grow big enough, all this should be moved - * to its own header file. - */ - -/* - * Figure out if we can use the ## preprocessor operator, which is supported - * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ - * or __cplusplus so we need to carefully check for them too. - */ - -#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ - defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ - defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ - defined(__ILEC400__) - /* This compiler is believed to have an ISO compatible preprocessor */ -#define CURL_ISOCPP -#else - /* This compiler is believed NOT to have an ISO compatible preprocessor */ -#undef CURL_ISOCPP -#endif - -/* - * Macros for minimum-width signed and unsigned curl_off_t integer constants. - */ - -#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) -# define CURLINC_OFF_T_C_HLPR2(x) x -# define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x) -# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ - CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ - CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) -#else -# ifdef CURL_ISOCPP -# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix -# else -# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix -# endif -# define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix) -# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) -#endif - #endif /* CURLINC_SYSTEM_H */ diff --git a/curl/typecheck-gcc.h b/curl/typecheck-gcc.h index e532e69..f2e4542 100644 --- a/curl/typecheck-gcc.h +++ b/curl/typecheck-gcc.h @@ -24,141 +24,248 @@ * ***************************************************************************/ -/* wraps curl_easy_setopt() with typechecking */ +/* wraps curl_easy_setopt() with type checking */ /* To add a new kind of warning, add an * if(curlcheck_sometype_option(_curl_opt)) * if(!curlcheck_sometype(value)) - * _curl_easy_setopt_err_sometype(); + * Wcurl_easy_setopt_err_sometype(); * block and define curlcheck_sometype_option, curlcheck_sometype and - * _curl_easy_setopt_err_sometype below + * Wcurl_easy_setopt_err_sometype below * * NOTE: We use two nested 'if' statements here instead of the && operator, in * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x * when compiling with -Wlogical-op. * - * To add an option that uses the same type as an existing option, you will - * just need to extend the appropriate _curl_*_option macro + * To add an option that uses the same type as an existing option, you need + * to extend the appropriate _curl_*_option macro */ + #define curl_easy_setopt(handle, option, value) \ __extension__({ \ - CURLoption _curl_opt = (option); \ - if(__builtin_constant_p(_curl_opt)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_long_option(_curl_opt)) \ - if(!curlcheck_long(value)) \ - _curl_easy_setopt_err_long(); \ - if(curlcheck_off_t_option(_curl_opt)) \ - if(!curlcheck_off_t(value)) \ - _curl_easy_setopt_err_curl_off_t(); \ - if(curlcheck_string_option(_curl_opt)) \ - if(!curlcheck_string(value)) \ - _curl_easy_setopt_err_string(); \ - if(curlcheck_write_cb_option(_curl_opt)) \ - if(!curlcheck_write_cb(value)) \ - _curl_easy_setopt_err_write_callback(); \ - if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!curlcheck_resolver_start_callback(value)) \ - _curl_easy_setopt_err_resolver_start_callback(); \ - if((_curl_opt) == CURLOPT_READFUNCTION) \ - if(!curlcheck_read_cb(value)) \ - _curl_easy_setopt_err_read_cb(); \ - if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ - if(!curlcheck_ioctl_cb(value)) \ - _curl_easy_setopt_err_ioctl_cb(); \ - if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ - if(!curlcheck_sockopt_cb(value)) \ - _curl_easy_setopt_err_sockopt_cb(); \ - if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!curlcheck_opensocket_cb(value)) \ - _curl_easy_setopt_err_opensocket_cb(); \ - if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ - if(!curlcheck_progress_cb(value)) \ - _curl_easy_setopt_err_progress_cb(); \ - if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ - if(!curlcheck_debug_cb(value)) \ - _curl_easy_setopt_err_debug_cb(); \ - if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!curlcheck_ssl_ctx_cb(value)) \ - _curl_easy_setopt_err_ssl_ctx_cb(); \ - if(curlcheck_conv_cb_option(_curl_opt)) \ - if(!curlcheck_conv_cb(value)) \ - _curl_easy_setopt_err_conv_cb(); \ - if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ - if(!curlcheck_seek_cb(value)) \ - _curl_easy_setopt_err_seek_cb(); \ - if(curlcheck_cb_data_option(_curl_opt)) \ - if(!curlcheck_cb_data(value)) \ - _curl_easy_setopt_err_cb_data(); \ - if((_curl_opt) == CURLOPT_ERRORBUFFER) \ - if(!curlcheck_error_buffer(value)) \ - _curl_easy_setopt_err_error_buffer(); \ - if((_curl_opt) == CURLOPT_STDERR) \ - if(!curlcheck_FILE(value)) \ - _curl_easy_setopt_err_FILE(); \ - if(curlcheck_postfields_option(_curl_opt)) \ - if(!curlcheck_postfields(value)) \ - _curl_easy_setopt_err_postfields(); \ - if((_curl_opt) == CURLOPT_HTTPPOST) \ - if(!curlcheck_arr((value), struct curl_httppost)) \ - _curl_easy_setopt_err_curl_httpost(); \ - if((_curl_opt) == CURLOPT_MIMEPOST) \ - if(!curlcheck_ptr((value), curl_mime)) \ - _curl_easy_setopt_err_curl_mimepost(); \ - if(curlcheck_slist_option(_curl_opt)) \ - if(!curlcheck_arr((value), struct curl_slist)) \ - _curl_easy_setopt_err_curl_slist(); \ - if((_curl_opt) == CURLOPT_SHARE) \ - if(!curlcheck_ptr((value), CURLSH)) \ - _curl_easy_setopt_err_CURLSH(); \ - ) \ - } \ - curl_easy_setopt(handle, _curl_opt, value); \ - }) - -/* wraps curl_easy_getinfo() with typechecking */ + if(__builtin_constant_p(option)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(option)) \ + if(!curlcheck_string(value)) \ + Wcurl_easy_setopt_err_string(); \ + if((option) == CURLOPT_PRIVATE) { } \ + if(curlcheck_write_cb_option(option)) \ + if(!curlcheck_write_cb(value)) \ + Wcurl_easy_setopt_err_write_callback(); \ + if(curlcheck_curl_option(option)) \ + if(!curlcheck_curl(value)) \ + Wcurl_easy_setopt_err_curl(); \ + if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + Wcurl_easy_setopt_err_resolver_start_callback(); \ + if((option) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + Wcurl_easy_setopt_err_read_cb(); \ + if((option) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + Wcurl_easy_setopt_err_ioctl_cb(); \ + if((option) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + Wcurl_easy_setopt_err_sockopt_cb(); \ + if((option) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + Wcurl_easy_setopt_err_opensocket_cb(); \ + if((option) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + Wcurl_easy_setopt_err_progress_cb(); \ + if((option) == CURLOPT_XFERINFOFUNCTION) \ + if(!curlcheck_xferinfo_cb(value)) \ + Wcurl_easy_setopt_err_xferinfo_cb(); \ + if((option) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + Wcurl_easy_setopt_err_debug_cb(); \ + if((option) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + Wcurl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(option)) \ + if(!curlcheck_conv_cb(value)) \ + Wcurl_easy_setopt_err_conv_cb(); \ + if((option) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + Wcurl_easy_setopt_err_seek_cb(); \ + if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ + if(!curlcheck_chunk_bgn_cb(value)) \ + Wcurl_easy_setopt_err_chunk_bgn_cb(); \ + if((option) == CURLOPT_CHUNK_END_FUNCTION) \ + if(!curlcheck_chunk_end_cb(value)) \ + Wcurl_easy_setopt_err_chunk_end_cb(); \ + if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ + if(!curlcheck_close_socket_cb(value)) \ + Wcurl_easy_setopt_err_close_socket_cb(); \ + if((option) == CURLOPT_FNMATCH_FUNCTION) \ + if(!curlcheck_fnmatch_cb(value)) \ + Wcurl_easy_setopt_err_fnmatch_cb(); \ + if((option) == CURLOPT_HSTSREADFUNCTION) \ + if(!curlcheck_hstsread_cb(value)) \ + Wcurl_easy_setopt_err_hstsread_cb(); \ + if((option) == CURLOPT_HSTSWRITEFUNCTION) \ + if(!curlcheck_hstswrite_cb(value)) \ + Wcurl_easy_setopt_err_hstswrite_cb(); \ + if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ + if(!curlcheck_ssh_hostkey_cb(value)) \ + Wcurl_easy_setopt_err_ssh_hostkey_cb(); \ + if((option) == CURLOPT_SSH_KEYFUNCTION) \ + if(!curlcheck_ssh_key_cb(value)) \ + Wcurl_easy_setopt_err_ssh_key_cb(); \ + if((option) == CURLOPT_INTERLEAVEFUNCTION) \ + if(!curlcheck_interleave_cb(value)) \ + Wcurl_easy_setopt_err_interleave_cb(); \ + if((option) == CURLOPT_PREREQFUNCTION) \ + if(!curlcheck_prereq_cb(value)) \ + Wcurl_easy_setopt_err_prereq_cb(); \ + if((option) == CURLOPT_TRAILERFUNCTION) \ + if(!curlcheck_trailer_cb(value)) \ + Wcurl_easy_setopt_err_trailer_cb(); \ + if(curlcheck_cb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_easy_setopt_err_cb_data(); \ + if((option) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + Wcurl_easy_setopt_err_error_buffer(); \ + if((option) == CURLOPT_CURLU) \ + if(!curlcheck_ptr((value), CURLU)) \ + Wcurl_easy_setopt_err_curlu(); \ + if((option) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + Wcurl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(option)) \ + if(!curlcheck_postfields(value)) \ + Wcurl_easy_setopt_err_postfields(); \ + if((option) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + Wcurl_easy_setopt_err_curl_httpost(); \ + if((option) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + Wcurl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(option)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + Wcurl_easy_setopt_err_curl_slist(); \ + if((option) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + Wcurl_easy_setopt_err_CURLSH(); \ + ) \ + } \ + (curl_easy_setopt)(handle, option, value); \ + }) + +/* wraps curl_easy_getinfo() with type checking */ #define curl_easy_getinfo(handle, info, arg) \ __extension__({ \ - CURLINFO _curl_info = (info); \ - if(__builtin_constant_p(_curl_info)) { \ - CURL_IGNORE_DEPRECATION( \ - if(curlcheck_string_info(_curl_info)) \ - if(!curlcheck_arr((arg), char *)) \ - _curl_easy_getinfo_err_string(); \ - if(curlcheck_long_info(_curl_info)) \ - if(!curlcheck_arr((arg), long)) \ - _curl_easy_getinfo_err_long(); \ - if(curlcheck_double_info(_curl_info)) \ - if(!curlcheck_arr((arg), double)) \ - _curl_easy_getinfo_err_double(); \ - if(curlcheck_slist_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_slist *)) \ - _curl_easy_getinfo_err_curl_slist(); \ - if(curlcheck_tlssessioninfo_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ - _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ - if(curlcheck_certinfo_info(_curl_info)) \ - if(!curlcheck_arr((arg), struct curl_certinfo *)) \ - _curl_easy_getinfo_err_curl_certinfo(); \ - if(curlcheck_socket_info(_curl_info)) \ - if(!curlcheck_arr((arg), curl_socket_t)) \ - _curl_easy_getinfo_err_curl_socket(); \ - if(curlcheck_off_t_info(_curl_info)) \ - if(!curlcheck_arr((arg), curl_off_t)) \ - _curl_easy_getinfo_err_curl_off_t(); \ - ) \ - } \ - curl_easy_getinfo(handle, _curl_info, arg); \ - }) + if(__builtin_constant_p(info)) { \ + CURL_IGNORE_DEPRECATION( \ + if(curlcheck_string_info(info)) \ + if(!curlcheck_arr((arg), char *)) \ + Wcurl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(info)) \ + if(!curlcheck_arr((arg), long)) \ + Wcurl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(info)) \ + if(!curlcheck_arr((arg), double)) \ + Wcurl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + Wcurl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + Wcurl_easy_getinfo_err_curl_tlssessioninfo(); \ + if(curlcheck_certinfo_info(info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + Wcurl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + Wcurl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + Wcurl_easy_getinfo_err_curl_off_t(); \ + ) \ + } \ + (curl_easy_getinfo)(handle, info, arg); \ + }) + +#define curl_multi_setopt(handle, option, value) \ + __extension__({ \ + if(__builtin_constant_p(option)) { \ + if(curlcheck_long_option(option)) \ + if(!curlcheck_long(value)) \ + Wcurl_multi_setopt_err_long(); \ + if(curlcheck_off_t_option(option)) \ + if(!curlcheck_off_t(value)) \ + Wcurl_multi_setopt_err_curl_off_t(); \ + if(curlcheck_multicb_data_option(option)) \ + if(!curlcheck_cb_data(value)) \ + Wcurl_multi_setopt_err_cb_data(); \ + if(curlcheck_charpp_option(option)) \ + if(!curlcheck_ptrptr(value, char)) \ + Wcurl_multi_setopt_err_charpp(); \ + if((option) == CURLMOPT_NOTIFYFUNCTION) \ + if(!curlcheck_multinotify_cb(value)) \ + Wcurl_multi_setopt_err_notifycb(); \ + if((option) == CURLMOPT_PUSHFUNCTION) \ + if(!curlcheck_multipush_cb(value)) \ + Wcurl_multi_setopt_err_pushcb(); \ + if((option) == CURLMOPT_SOCKETFUNCTION) \ + if(!curlcheck_multisocket_cb(value)) \ + Wcurl_multi_setopt_err_socketcb(); \ + if((option) == CURLMOPT_TIMERFUNCTION) \ + if(!curlcheck_multitimer_cb(value)) \ + Wcurl_multi_setopt_err_timercb(); \ + } \ + (curl_multi_setopt)(handle, option, value); \ + }) + +/* evaluates to true if the option takes a data argument to pass to a + callback */ +#define curlcheck_multicb_data_option(option) \ + ((option) == CURLMOPT_NOTIFYDATA || \ + (option) == CURLMOPT_PUSHDATA || \ + (option) == CURLMOPT_SOCKETDATA || \ + (option) == CURLMOPT_TIMERDATA || \ + 0) + +/* evaluates to true if the option takes a char ** argument */ +#define curlcheck_charpp_option(option) \ + ((option) == CURLMOPT_PIPELINING_SERVER_BL || \ + (option) == CURLMOPT_PIPELINING_SITE_BL || \ + 0) + +/* evaluates to true if expr is of type curl_multi_timer_callback */ +#define curlcheck_multitimer_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_multi_timer_callback)) + +/* evaluates to true if expr is of type curl_socket_callback */ +#define curlcheck_multisocket_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_socket_callback)) + +/* evaluates to true if expr is of type curl_push_callback */ +#define curlcheck_multipush_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_push_callback)) + +/* evaluates to true if expr is of type curl_push_callback */ +#define curlcheck_multinotify_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_notify_callback)) /* - * For now, just make sure that the functions are called with three arguments + * Make sure that the functions are called with three arguments */ -#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) -#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#define curl_share_setopt(share, opt, param) \ + (curl_share_setopt)(share, opt, param) - -/* the actual warnings, triggered by calling the _curl_easy_setopt_err* +/* the actual warnings, triggered by calling the Wcurl_easy_setopt_err* * functions */ /* To define a new warning, use _CURL_WARNING(identifier, "message") */ @@ -167,188 +274,233 @@ __attribute__((__unused__)) __attribute__((__noinline__)) \ id(void) { __asm__(""); } -CURLWARNING(_curl_easy_setopt_err_long, - "curl_easy_setopt expects a long argument for this option") -CURLWARNING(_curl_easy_setopt_err_curl_off_t, - "curl_easy_setopt expects a curl_off_t argument for this option") -CURLWARNING(_curl_easy_setopt_err_string, - "curl_easy_setopt expects a " - "string ('char *' or char[]) argument for this option" - ) -CURLWARNING(_curl_easy_setopt_err_write_callback, - "curl_easy_setopt expects a curl_write_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_resolver_start_callback, - "curl_easy_setopt expects a " - "curl_resolver_start_callback argument for this option" - ) -CURLWARNING(_curl_easy_setopt_err_read_cb, - "curl_easy_setopt expects a curl_read_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_ioctl_cb, - "curl_easy_setopt expects a curl_ioctl_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_sockopt_cb, - "curl_easy_setopt expects a curl_sockopt_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_opensocket_cb, - "curl_easy_setopt expects a " - "curl_opensocket_callback argument for this option" - ) -CURLWARNING(_curl_easy_setopt_err_progress_cb, - "curl_easy_setopt expects a curl_progress_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_debug_cb, - "curl_easy_setopt expects a curl_debug_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb, - "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_conv_cb, - "curl_easy_setopt expects a curl_conv_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_seek_cb, - "curl_easy_setopt expects a curl_seek_callback argument for this option") -CURLWARNING(_curl_easy_setopt_err_cb_data, - "curl_easy_setopt expects a " - "private data pointer as argument for this option") -CURLWARNING(_curl_easy_setopt_err_error_buffer, - "curl_easy_setopt expects a " - "char buffer of CURL_ERROR_SIZE as argument for this option") -CURLWARNING(_curl_easy_setopt_err_FILE, - "curl_easy_setopt expects a 'FILE *' argument for this option") -CURLWARNING(_curl_easy_setopt_err_postfields, - "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") -CURLWARNING(_curl_easy_setopt_err_curl_httpost, - "curl_easy_setopt expects a 'struct curl_httppost *' " - "argument for this option") -CURLWARNING(_curl_easy_setopt_err_curl_mimepost, - "curl_easy_setopt expects a 'curl_mime *' " - "argument for this option") -CURLWARNING(_curl_easy_setopt_err_curl_slist, - "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") -CURLWARNING(_curl_easy_setopt_err_CURLSH, - "curl_easy_setopt expects a CURLSH* argument for this option") - -CURLWARNING(_curl_easy_getinfo_err_string, - "curl_easy_getinfo expects a pointer to 'char *' for this info") -CURLWARNING(_curl_easy_getinfo_err_long, - "curl_easy_getinfo expects a pointer to long for this info") -CURLWARNING(_curl_easy_getinfo_err_double, - "curl_easy_getinfo expects a pointer to double for this info") -CURLWARNING(_curl_easy_getinfo_err_curl_slist, - "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") -CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, - "curl_easy_getinfo expects a pointer to " - "'struct curl_tlssessioninfo *' for this info") -CURLWARNING(_curl_easy_getinfo_err_curl_certinfo, - "curl_easy_getinfo expects a pointer to " - "'struct curl_certinfo *' for this info") -CURLWARNING(_curl_easy_getinfo_err_curl_socket, - "curl_easy_getinfo expects a pointer to curl_socket_t for this info") -CURLWARNING(_curl_easy_getinfo_err_curl_off_t, - "curl_easy_getinfo expects a pointer to curl_off_t for this info") +CURLWARNING(Wcurl_multi_setopt_err_long, + "curl_multi_setopt expects a long argument") +CURLWARNING(Wcurl_multi_setopt_err_curl_off_t, + "curl_multi_setopt expects a curl_off_t argument") +CURLWARNING(Wcurl_multi_setopt_err_cb_data, + "curl_multi_setopt expects a 'void *' argument") +CURLWARNING(Wcurl_multi_setopt_err_charpp, + "curl_multi_setopt expects a 'char **' argument") +CURLWARNING(Wcurl_multi_setopt_err_pushcb, + "curl_multi_setopt expects a curl_push_callback argument") +CURLWARNING(Wcurl_multi_setopt_err_notifycb, + "curl_multi_setopt expects a curl_notify_callback argument") +CURLWARNING(Wcurl_multi_setopt_err_socketcb, + "curl_multi_setopt expects a curl_socket_callback argument") +CURLWARNING(Wcurl_multi_setopt_err_timercb, + "curl_multi_setopt expects a curl_multi_timer_callback argument") + +CURLWARNING(Wcurl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument") +CURLWARNING(Wcurl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument") +CURLWARNING(Wcurl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string ('char *' or char[]) argument") +CURLWARNING(Wcurl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_resolver_start_callback, + "curl_easy_setopt expects a " + "curl_resolver_start_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_xferinfo_cb, + "curl_easy_setopt expects a curl_xferinfo_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument") +CURLWARNING(Wcurl_easy_setopt_err_chunk_bgn_cb, + "curl_easy_setopt expects a curl_chunk_bgn_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_chunk_end_cb, + "curl_easy_setopt expects a curl_chunk_end_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_close_socket_cb, + "curl_easy_setopt expects a curl_closesocket_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_fnmatch_cb, + "curl_easy_setopt expects a curl_fnmatch_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_hstsread_cb, + "curl_easy_setopt expects a curl_hstsread_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_hstswrite_cb, + "curl_easy_setopt expects a curl_hstswrite_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_ssh_key_cb, + "curl_easy_setopt expects a curl_sshkeycallback argument") +CURLWARNING(Wcurl_easy_setopt_err_ssh_hostkey_cb, + "curl_easy_setopt expects a curl_sshhostkeycallback argument") +CURLWARNING(Wcurl_easy_setopt_err_interleave_cb, + "curl_easy_setopt expects a curl_interleave_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_prereq_cb, + "curl_easy_setopt expects a curl_prereq_callback argument") +CURLWARNING(Wcurl_easy_setopt_err_trailer_cb, + "curl_easy_setopt expects a curl_trailerfunc_ok argument") +CURLWARNING(Wcurl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument") +CURLWARNING(Wcurl_easy_setopt_err_curlu, + "curl_easy_setopt expects a 'CURLU *' argument") +CURLWARNING(Wcurl_easy_setopt_err_curl, + "curl_easy_setopt expects a 'CURL *' argument") +CURLWARNING(Wcurl_easy_setopt_err_FILE, + "curl_easy_setopt expects a 'FILE *' argument") +CURLWARNING(Wcurl_easy_setopt_err_postfields, + "curl_easy_setopt expects a 'void *' or 'char *' argument") +CURLWARNING(Wcurl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument") +CURLWARNING(Wcurl_easy_setopt_err_curl_mimepost, + "curl_easy_setopt expects a 'curl_mime *' " + "argument") +CURLWARNING(Wcurl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a 'struct curl_slist *' argument") +CURLWARNING(Wcurl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument") +CURLWARNING(Wcurl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to 'char *'") +CURLWARNING(Wcurl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long") +CURLWARNING(Wcurl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double") +CURLWARNING(Wcurl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to 'struct curl_slist *'") +CURLWARNING(Wcurl_easy_getinfo_err_curl_tlssessioninfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_tlssessioninfo *'") +CURLWARNING(Wcurl_easy_getinfo_err_curl_certinfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_certinfo *'") +CURLWARNING(Wcurl_easy_getinfo_err_curl_socket, + "curl_easy_getinfo expects a pointer to curl_socket_t") +CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, + "curl_easy_getinfo expects a pointer to curl_off_t") /* groups of curl_easy_setops options that take the same type of argument */ -/* To add a new option to one of the groups, just add - * (option) == CURLOPT_SOMETHING - * to the or-expression. If the option takes a long or curl_off_t, you do not - * have to do anything - */ - /* evaluates to true if option takes a long argument */ -#define curlcheck_long_option(option) \ +#define curlcheck_long_option(option) \ (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) -#define curlcheck_off_t_option(option) \ +#define curlcheck_off_t_option(option) \ (((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB)) +/* option takes a CURL * argument */ +#define curlcheck_curl_option(option) \ + ((option) == CURLOPT_STREAM_DEPENDS || \ + (option) == CURLOPT_STREAM_DEPENDS_E || \ + 0) + /* evaluates to true if option takes a char* argument */ -#define curlcheck_string_option(option) \ - ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ - (option) == CURLOPT_ACCEPT_ENCODING || \ - (option) == CURLOPT_ALTSVC || \ - (option) == CURLOPT_CAINFO || \ - (option) == CURLOPT_CAPATH || \ - (option) == CURLOPT_COOKIE || \ - (option) == CURLOPT_COOKIEFILE || \ - (option) == CURLOPT_COOKIEJAR || \ - (option) == CURLOPT_COOKIELIST || \ - (option) == CURLOPT_CRLFILE || \ - (option) == CURLOPT_CUSTOMREQUEST || \ - (option) == CURLOPT_DEFAULT_PROTOCOL || \ - (option) == CURLOPT_DNS_INTERFACE || \ - (option) == CURLOPT_DNS_LOCAL_IP4 || \ - (option) == CURLOPT_DNS_LOCAL_IP6 || \ - (option) == CURLOPT_DNS_SERVERS || \ - (option) == CURLOPT_DOH_URL || \ - (option) == CURLOPT_ECH || \ - (option) == CURLOPT_EGDSOCKET || \ - (option) == CURLOPT_FTP_ACCOUNT || \ - (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ - (option) == CURLOPT_FTPPORT || \ - (option) == CURLOPT_HSTS || \ - (option) == CURLOPT_HAPROXY_CLIENT_IP || \ - (option) == CURLOPT_INTERFACE || \ - (option) == CURLOPT_ISSUERCERT || \ - (option) == CURLOPT_KEYPASSWD || \ - (option) == CURLOPT_KRBLEVEL || \ - (option) == CURLOPT_LOGIN_OPTIONS || \ - (option) == CURLOPT_MAIL_AUTH || \ - (option) == CURLOPT_MAIL_FROM || \ - (option) == CURLOPT_NETRC_FILE || \ - (option) == CURLOPT_NOPROXY || \ - (option) == CURLOPT_PASSWORD || \ - (option) == CURLOPT_PINNEDPUBLICKEY || \ - (option) == CURLOPT_PRE_PROXY || \ - (option) == CURLOPT_PROTOCOLS_STR || \ - (option) == CURLOPT_PROXY || \ - (option) == CURLOPT_PROXY_CAINFO || \ - (option) == CURLOPT_PROXY_CAPATH || \ - (option) == CURLOPT_PROXY_CRLFILE || \ - (option) == CURLOPT_PROXY_ISSUERCERT || \ - (option) == CURLOPT_PROXY_KEYPASSWD || \ - (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ - (option) == CURLOPT_PROXY_SERVICE_NAME || \ - (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ - (option) == CURLOPT_PROXY_SSLCERT || \ - (option) == CURLOPT_PROXY_SSLCERTTYPE || \ - (option) == CURLOPT_PROXY_SSLKEY || \ - (option) == CURLOPT_PROXY_SSLKEYTYPE || \ - (option) == CURLOPT_PROXY_TLS13_CIPHERS || \ - (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ - (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ - (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ - (option) == CURLOPT_PROXYPASSWORD || \ - (option) == CURLOPT_PROXYUSERNAME || \ - (option) == CURLOPT_PROXYUSERPWD || \ - (option) == CURLOPT_RANDOM_FILE || \ - (option) == CURLOPT_RANGE || \ - (option) == CURLOPT_REDIR_PROTOCOLS_STR || \ - (option) == CURLOPT_REFERER || \ - (option) == CURLOPT_REQUEST_TARGET || \ - (option) == CURLOPT_RTSP_SESSION_ID || \ - (option) == CURLOPT_RTSP_STREAM_URI || \ - (option) == CURLOPT_RTSP_TRANSPORT || \ - (option) == CURLOPT_SASL_AUTHZID || \ - (option) == CURLOPT_SERVICE_NAME || \ - (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ - (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ - (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \ - (option) == CURLOPT_SSH_KNOWNHOSTS || \ - (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ - (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ - (option) == CURLOPT_SSLCERT || \ - (option) == CURLOPT_SSLCERTTYPE || \ - (option) == CURLOPT_SSLENGINE || \ - (option) == CURLOPT_SSLKEY || \ - (option) == CURLOPT_SSLKEYTYPE || \ - (option) == CURLOPT_SSL_CIPHER_LIST || \ - (option) == CURLOPT_TLS13_CIPHERS || \ - (option) == CURLOPT_TLSAUTH_PASSWORD || \ - (option) == CURLOPT_TLSAUTH_TYPE || \ - (option) == CURLOPT_TLSAUTH_USERNAME || \ - (option) == CURLOPT_UNIX_SOCKET_PATH || \ - (option) == CURLOPT_URL || \ - (option) == CURLOPT_USERAGENT || \ - (option) == CURLOPT_USERNAME || \ - (option) == CURLOPT_AWS_SIGV4 || \ - (option) == CURLOPT_USERPWD || \ - (option) == CURLOPT_XOAUTH2_BEARER || \ - (option) == CURLOPT_SSL_EC_CURVES || \ +#define curlcheck_string_option(option) \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ + (option) == CURLOPT_ACCEPT_ENCODING || \ + (option) == CURLOPT_ALTSVC || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DOH_URL || \ + (option) == CURLOPT_ECH || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_HAPROXY_CLIENT_IP || \ + (option) == CURLOPT_HSTS || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_ISSUERCERT || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ + (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PRE_PROXY || \ + (option) == CURLOPT_PROTOCOLS_STR || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXY_CAINFO || \ + (option) == CURLOPT_PROXY_CAPATH || \ + (option) == CURLOPT_PROXY_CRLFILE || \ + (option) == CURLOPT_PROXY_ISSUERCERT || \ + (option) == CURLOPT_PROXY_KEYPASSWD || \ + (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_SSLCERT || \ + (option) == CURLOPT_PROXY_SSLCERTTYPE || \ + (option) == CURLOPT_PROXY_SSLKEY || \ + (option) == CURLOPT_PROXY_SSLKEYTYPE || \ + (option) == CURLOPT_PROXY_TLS13_CIPHERS || \ + (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REDIR_PROTOCOLS_STR || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_REQUEST_TARGET || \ + (option) == CURLOPT_RTSP_SESSION_ID || \ + (option) == CURLOPT_RTSP_STREAM_URI || \ + (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SASL_AUTHZID || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_SSL_EC_CURVES || \ + (option) == CURLOPT_SSL_SIGNATURE_ALGORITHMS || \ + (option) == CURLOPT_TLS13_CIPHERS || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_AWS_SIGV4 || \ + (option) == CURLOPT_HTTPSIG_HEADERS || \ + (option) == CURLOPT_HTTPSIG_KEY || \ + (option) == CURLOPT_HTTPSIG_KEYID || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ 0) /* evaluates to true if option takes a curl_write_callback argument */ @@ -363,86 +515,89 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) /* evaluates to true if option takes a data argument to pass to a callback */ -#define curlcheck_cb_data_option(option) \ - ((option) == CURLOPT_CHUNK_DATA || \ - (option) == CURLOPT_CLOSESOCKETDATA || \ - (option) == CURLOPT_DEBUGDATA || \ - (option) == CURLOPT_FNMATCH_DATA || \ - (option) == CURLOPT_HEADERDATA || \ - (option) == CURLOPT_HSTSREADDATA || \ - (option) == CURLOPT_HSTSWRITEDATA || \ - (option) == CURLOPT_INTERLEAVEDATA || \ - (option) == CURLOPT_IOCTLDATA || \ - (option) == CURLOPT_OPENSOCKETDATA || \ - (option) == CURLOPT_PREREQDATA || \ - (option) == CURLOPT_PROGRESSDATA || \ - (option) == CURLOPT_READDATA || \ - (option) == CURLOPT_SEEKDATA || \ - (option) == CURLOPT_SOCKOPTDATA || \ - (option) == CURLOPT_SSH_KEYDATA || \ - (option) == CURLOPT_SSL_CTX_DATA || \ - (option) == CURLOPT_WRITEDATA || \ - (option) == CURLOPT_RESOLVER_START_DATA || \ - (option) == CURLOPT_TRAILERDATA || \ - (option) == CURLOPT_SSH_HOSTKEYDATA || \ +#define curlcheck_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_HSTSREADDATA || \ + (option) == CURLOPT_HSTSWRITEDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PREREQDATA || \ + (option) == CURLOPT_XFERINFODATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_TRAILERDATA || \ + (option) == CURLOPT_SSH_HOSTKEYDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ -#define curlcheck_postfields_option(option) \ - ((option) == CURLOPT_POSTFIELDS || \ - (option) == CURLOPT_COPYPOSTFIELDS || \ +#define curlcheck_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ 0) /* evaluates to true if option takes a struct curl_slist * argument */ -#define curlcheck_slist_option(option) \ - ((option) == CURLOPT_HTTP200ALIASES || \ - (option) == CURLOPT_HTTPHEADER || \ - (option) == CURLOPT_MAIL_RCPT || \ - (option) == CURLOPT_POSTQUOTE || \ - (option) == CURLOPT_PREQUOTE || \ - (option) == CURLOPT_PROXYHEADER || \ - (option) == CURLOPT_QUOTE || \ - (option) == CURLOPT_RESOLVE || \ - (option) == CURLOPT_TELNETOPTIONS || \ - (option) == CURLOPT_CONNECT_TO || \ +#define curlcheck_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + (option) == CURLOPT_CONNECT_TO || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ /* evaluates to true if info expects a pointer to char * argument */ -#define curlcheck_string_info(info) \ - (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ +#define curlcheck_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG && \ (info) != CURLINFO_PRIVATE) /* evaluates to true if info expects a pointer to long argument */ -#define curlcheck_long_info(info) \ +#define curlcheck_long_info(info) \ (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) /* evaluates to true if info expects a pointer to double argument */ -#define curlcheck_double_info(info) \ +#define curlcheck_double_info(info) \ (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) /* true if info expects a pointer to struct curl_slist * argument */ #define curlcheck_slist_info(info) \ - (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + (((info) == CURLINFO_SSL_ENGINES) || \ + ((info) == CURLINFO_COOKIELIST)) /* true if info expects a pointer to struct curl_tlssessioninfo * argument */ -#define curlcheck_tlssessioninfo_info(info) \ - (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) +#define curlcheck_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || \ + ((info) == CURLINFO_TLS_SESSION)) /* true if info expects a pointer to struct curl_certinfo * argument */ #define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) /* true if info expects a pointer to struct curl_socket_t argument */ -#define curlcheck_socket_info(info) \ +#define curlcheck_socket_info(info) \ (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) /* true if info expects a pointer to curl_off_t argument */ -#define curlcheck_off_t_info(info) \ +#define curlcheck_off_t_info(info) \ (CURLINFO_OFF_T < (info)) - -/* typecheck helpers -- check whether given expression has requested type */ +/* + * typecheck helpers -- check whether given expression has requested type + */ /* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, * otherwise define a new macro. Search for __builtin_types_compatible_p @@ -453,9 +608,10 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, * == or whatsoever. */ -/* XXX: should evaluate to true if expr is a pointer */ -#define curlcheck_any_ptr(expr) \ - (sizeof(expr) == sizeof(void *)) +/* XXX: should evaluate to true if expr is a pointer or a char[] array */ +#define curlcheck_any_ptr(expr) \ + (sizeof(expr) == sizeof(void *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) /* evaluates to true if expr is NULL */ /* XXX: must not evaluate expr, so this check is not accurate */ @@ -468,6 +624,14 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, __builtin_types_compatible_p(__typeof__(expr), type *) || \ __builtin_types_compatible_p(__typeof__(expr), const type *)) +/* evaluates to true if expr is type**, const type** or NULL */ +#define curlcheck_ptrptr(expr, type) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type **) || \ + __builtin_types_compatible_p(__typeof__(expr), type *[]) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *[]) || \ + __builtin_types_compatible_p(__typeof__(expr), const type **)) + /* evaluates to true if expr is one of type[], type*, NULL or const type* */ #define curlcheck_arr(expr, type) \ (curlcheck_ptr((expr), type) || \ @@ -479,29 +643,42 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, curlcheck_arr((expr), signed char) || \ curlcheck_arr((expr), unsigned char)) +/* evaluates to true if expr is a CURL * */ +#define curlcheck_curl(expr) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), CURL *)) + /* evaluates to true if expr is a long (no matter the signedness) * XXX: for now, int is also accepted (and therefore short and char, which * are promoted to int when passed to a variadic function) */ -#define curlcheck_long(expr) \ - (__builtin_types_compatible_p(__typeof__(expr), long) || \ - __builtin_types_compatible_p(__typeof__(expr), signed long) || \ - __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ - __builtin_types_compatible_p(__typeof__(expr), int) || \ - __builtin_types_compatible_p(__typeof__(expr), signed int) || \ - __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ - __builtin_types_compatible_p(__typeof__(expr), short) || \ - __builtin_types_compatible_p(__typeof__(expr), signed short) || \ - __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ - __builtin_types_compatible_p(__typeof__(expr), char) || \ - __builtin_types_compatible_p(__typeof__(expr), signed char) || \ - __builtin_types_compatible_p(__typeof__(expr), unsigned char)) +#define curlcheck_long(expr) \ + ( \ + ((sizeof(long) != sizeof(int)) && \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long))) \ + || \ + ((sizeof(long) == sizeof(int)) && \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char))) \ + ) /* evaluates to true if expr is of type curl_off_t */ -#define curlcheck_off_t(expr) \ +#define curlcheck_off_t(expr) \ (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ -/* XXX: also check size of an char[] array? */ +/* XXX: also check size of a char[] array? */ #define curlcheck_error_buffer(expr) \ (curlcheck_NULL(expr) || \ __builtin_types_compatible_p(__typeof__(expr), char *) || \ @@ -513,7 +690,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (curlcheck_ptr((expr), void) || \ curlcheck_ptr((expr), FILE)) #else /* be less strict */ -#define curlcheck_cb_data(expr) \ +#define curlcheck_cb_data(expr) \ curlcheck_any_ptr(expr) #endif @@ -535,8 +712,8 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, __builtin_types_compatible_p(__typeof__(func) *, type)) /* evaluates to true if expr is of type curl_resolver_start_callback */ -#define curlcheck_resolver_start_callback(expr) \ - (curlcheck_NULL(expr) || \ +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_resolver_start_callback)) /* evaluates to true if expr is of type curl_read_callback or "similar" */ @@ -544,118 +721,123 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), __typeof__(fread) *) || \ curlcheck_cb_compatible((expr), curl_read_callback) || \ - curlcheck_cb_compatible((expr), _curl_read_callback1) || \ - curlcheck_cb_compatible((expr), _curl_read_callback2) || \ - curlcheck_cb_compatible((expr), _curl_read_callback3) || \ - curlcheck_cb_compatible((expr), _curl_read_callback4) || \ - curlcheck_cb_compatible((expr), _curl_read_callback5) || \ - curlcheck_cb_compatible((expr), _curl_read_callback6)) -typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); -typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); -typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); -typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); -typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); -typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); + curlcheck_cb_compatible((expr), Wcurl_read_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback6)) +typedef size_t (*Wcurl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*Wcurl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*Wcurl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*Wcurl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*Wcurl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*Wcurl_read_callback6)(void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_write_callback or "similar" */ #define curlcheck_write_cb(expr) \ (curlcheck_read_cb(expr) || \ curlcheck_cb_compatible((expr), __typeof__(fwrite) *) || \ curlcheck_cb_compatible((expr), curl_write_callback) || \ - curlcheck_cb_compatible((expr), _curl_write_callback1) || \ - curlcheck_cb_compatible((expr), _curl_write_callback2) || \ - curlcheck_cb_compatible((expr), _curl_write_callback3) || \ - curlcheck_cb_compatible((expr), _curl_write_callback4) || \ - curlcheck_cb_compatible((expr), _curl_write_callback5) || \ - curlcheck_cb_compatible((expr), _curl_write_callback6)) -typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); -typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, - const void *); -typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); -typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); -typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, - const void *); -typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); + curlcheck_cb_compatible((expr), Wcurl_write_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback6)) +typedef size_t (*Wcurl_write_callback1)(const char *, size_t, size_t, void *); +typedef size_t (*Wcurl_write_callback2)(const char *, size_t, size_t, + const void *); +typedef size_t (*Wcurl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (*Wcurl_write_callback4)(const void *, size_t, size_t, void *); +typedef size_t (*Wcurl_write_callback5)(const void *, size_t, size_t, + const void *); +typedef size_t (*Wcurl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ #define curlcheck_ioctl_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_ioctl_callback) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback1) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback2) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback3) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback4)) -typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); -typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); -typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); -typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback4)) +typedef curlioerr (*Wcurl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*Wcurl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*Wcurl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*Wcurl_ioctl_callback4)(CURL *, curliocmd, const void *); /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ #define curlcheck_sockopt_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_sockopt_callback) || \ - curlcheck_cb_compatible((expr), _curl_sockopt_callback1) || \ - curlcheck_cb_compatible((expr), _curl_sockopt_callback2)) -typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); -typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, - curlsocktype); + curlcheck_cb_compatible((expr), Wcurl_sockopt_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_sockopt_callback2)) +typedef int (*Wcurl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (*Wcurl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); /* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ #define curlcheck_opensocket_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_opensocket_callback) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback1) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback2) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback3) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback4)) -typedef curl_socket_t (*_curl_opensocket_callback1) + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback4)) +typedef curl_socket_t (*Wcurl_opensocket_callback1) (void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback2) +typedef curl_socket_t (*Wcurl_opensocket_callback2) (void *, curlsocktype, const struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback3) +typedef curl_socket_t (*Wcurl_opensocket_callback3) (const void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback4) +typedef curl_socket_t (*Wcurl_opensocket_callback4) (const void *, curlsocktype, const struct curl_sockaddr *); /* evaluates to true if expr is of type curl_progress_callback or "similar" */ #define curlcheck_progress_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_progress_callback) || \ - curlcheck_cb_compatible((expr), _curl_progress_callback1) || \ - curlcheck_cb_compatible((expr), _curl_progress_callback2)) -typedef int (*_curl_progress_callback1)(void *, + curlcheck_cb_compatible((expr), Wcurl_progress_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_progress_callback2)) +typedef int (*Wcurl_progress_callback1)(void *, double, double, double, double); -typedef int (*_curl_progress_callback2)(const void *, +typedef int (*Wcurl_progress_callback2)(const void *, double, double, double, double); +/* evaluates to true if expr is of type curl_xferinfo_callback */ +#define curlcheck_xferinfo_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_xferinfo_callback)) + /* evaluates to true if expr is of type curl_debug_callback or "similar" */ #define curlcheck_debug_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_debug_callback) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback1) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback2) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback3) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback4) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback5) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback6) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback7) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback8)) -typedef int (*_curl_debug_callback1) (CURL *, + curlcheck_cb_compatible((expr), Wcurl_debug_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback6) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback7) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback8)) +typedef int (*Wcurl_debug_callback1)(CURL *, curl_infotype, char *, size_t, void *); -typedef int (*_curl_debug_callback2) (CURL *, +typedef int (*Wcurl_debug_callback2)(CURL *, curl_infotype, char *, size_t, const void *); -typedef int (*_curl_debug_callback3) (CURL *, +typedef int (*Wcurl_debug_callback3)(CURL *, curl_infotype, const char *, size_t, void *); -typedef int (*_curl_debug_callback4) (CURL *, +typedef int (*Wcurl_debug_callback4)(CURL *, curl_infotype, const char *, size_t, const void *); -typedef int (*_curl_debug_callback5) (CURL *, +typedef int (*Wcurl_debug_callback5)(CURL *, curl_infotype, unsigned char *, size_t, void *); -typedef int (*_curl_debug_callback6) (CURL *, +typedef int (*Wcurl_debug_callback6)(CURL *, curl_infotype, unsigned char *, size_t, const void *); -typedef int (*_curl_debug_callback7) (CURL *, +typedef int (*Wcurl_debug_callback7)(CURL *, curl_infotype, const unsigned char *, size_t, void *); -typedef int (*_curl_debug_callback8) (CURL *, +typedef int (*Wcurl_debug_callback8)(CURL *, curl_infotype, const unsigned char *, size_t, const void *); /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ @@ -663,56 +845,120 @@ typedef int (*_curl_debug_callback8) (CURL *, #define curlcheck_ssl_ctx_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8)) -typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); -typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback6) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback7) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback8)) +typedef CURLcode (*Wcurl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback4)(CURL *, const void *, const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX - * this will of course break if we are included before OpenSSL headers... + * this of course breaks if we are included before OpenSSL headers... */ -typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); -typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX *, +typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback8)(CURL *, const SSL_CTX *, const void *); #else -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback5; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback6; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback7; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback8; #endif /* evaluates to true if expr is of type curl_conv_callback or "similar" */ #define curlcheck_conv_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_conv_callback) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback1) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback2) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback3) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback4)) -typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); -typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); -typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); -typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + curlcheck_cb_compatible((expr), Wcurl_conv_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback4)) +typedef CURLcode (*Wcurl_conv_callback1)(char *, size_t length); +typedef CURLcode (*Wcurl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*Wcurl_conv_callback3)(void *, size_t length); +typedef CURLcode (*Wcurl_conv_callback4)(const void *, size_t length); /* evaluates to true if expr is of type curl_seek_callback or "similar" */ #define curlcheck_seek_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_seek_callback) || \ - curlcheck_cb_compatible((expr), _curl_seek_callback1) || \ - curlcheck_cb_compatible((expr), _curl_seek_callback2)) -typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); -typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + curlcheck_cb_compatible((expr), Wcurl_seek_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_seek_callback2)) +typedef CURLcode (*Wcurl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*Wcurl_seek_callback2)(const void *, curl_off_t, int); + +/* evaluates to true if expr is of type curl_chunk_bgn_callback */ +#define curlcheck_chunk_bgn_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_chunk_bgn_callback) || \ + curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback2)) +typedef long (*Wcurl_chunk_bgn_callback1)(struct curl_fileinfo *, + void *, int); +typedef long (*Wcurl_chunk_bgn_callback2)(void *, void *, int); + +/* evaluates to true if expr is of type curl_chunk_end_callback */ +#define curlcheck_chunk_end_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_chunk_end_callback)) + +/* evaluates to true if expr is of type curl_closesocket_callback */ +#define curlcheck_close_socket_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_closesocket_callback)) + +/* evaluates to true if expr is of type curl_fnmatch_callback */ +#define curlcheck_fnmatch_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_fnmatch_callback)) +/* evaluates to true if expr is of type curl_hstsread_callback */ +#define curlcheck_hstsread_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_hstsread_callback)) + +/* evaluates to true if expr is of type curl_hstswrite_callback */ +#define curlcheck_hstswrite_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_hstswrite_callback)) + +/* evaluates to true if expr is of type curl_sshhostkeycallback */ +#define curlcheck_ssh_hostkey_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_sshhostkeycallback)) + +/* evaluates to true if expr is of type curl_sshkeycallback */ +#define curlcheck_ssh_key_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_sshkeycallback)) + +/* evaluates to true if expr is of type curl_interleave_callback */ +#define curlcheck_interleave_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), Wcurl_interleave_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_interleave_callback2)) +typedef size_t (*Wcurl_interleave_callback1)(void *p, size_t s, + size_t n, void *u); +typedef size_t (*Wcurl_interleave_callback2)(char *p, size_t s, + size_t n, void *u); + +/* evaluates to true if expr is of type curl_prereq_callback */ +#define curlcheck_prereq_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_prereq_callback)) + +/* evaluates to true if expr is of type curl_trailer_callback */ +#define curlcheck_trailer_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_trailer_callback)) #endif /* CURLINC_TYPECHECK_GCC_H */ diff --git a/curl/urlapi.h b/curl/urlapi.h index b4a6e5d..02553df 100644 --- a/curl/urlapi.h +++ b/curl/urlapi.h @@ -26,7 +26,7 @@ #include "curl.h" -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -64,6 +64,7 @@ typedef enum { CURLUE_BAD_USER, /* 29 */ CURLUE_LACKS_IDN, /* 30 */ CURLUE_TOO_LARGE, /* 31 */ + CURLUE_BACKSLASH, /* 32 */ CURLUE_LAST } CURLUcode; @@ -81,28 +82,28 @@ typedef enum { CURLUPART_ZONEID /* added in 7.65.0 */ } CURLUPart; -#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ -#define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, - if the port number matches the - default for the scheme */ -#define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if - missing */ -#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ -#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ -#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ -#define CURLU_URLDECODE (1<<6) /* URL decode on get */ -#define CURLU_URLENCODE (1<<7) /* URL encode on set */ -#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ -#define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ -#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the - scheme is unknown. */ -#define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the hostname in punycode */ -#define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ -#define CURLU_GET_EMPTY (1<<14) /* allow empty queries and fragments - when extracting the URL or the - components */ -#define CURLU_NO_GUESS_SCHEME (1<<15) /* for get, do not accept a guess */ +#define CURLU_DEFAULT_PORT (1 << 0) /* return default port number */ +#define CURLU_NO_DEFAULT_PORT (1 << 1) /* act as if no port number was set, + if the port number matches the + default for the scheme */ +#define CURLU_DEFAULT_SCHEME (1 << 2) /* return default scheme if + missing */ +#define CURLU_NON_SUPPORT_SCHEME (1 << 3) /* allow non-supported scheme */ +#define CURLU_PATH_AS_IS (1 << 4) /* leave dot sequences */ +#define CURLU_DISALLOW_USER (1 << 5) /* no user+password allowed */ +#define CURLU_URLDECODE (1 << 6) /* URL decode on get */ +#define CURLU_URLENCODE (1 << 7) /* URL encode on set */ +#define CURLU_APPENDQUERY (1 << 8) /* append a form style part */ +#define CURLU_GUESS_SCHEME (1 << 9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1 << 10) /* Allow empty authority when the + scheme is unknown. */ +#define CURLU_ALLOW_SPACE (1 << 11) /* Allow spaces in the URL */ +#define CURLU_PUNYCODE (1 << 12) /* get the hostname in punycode */ +#define CURLU_PUNY2IDN (1 << 13) /* punycode => IDN conversion */ +#define CURLU_GET_EMPTY (1 << 14) /* allow empty queries and fragments + when extracting the URL or the + components */ +#define CURLU_NO_GUESS_SCHEME (1 << 15) /* for get, do not accept a guess */ typedef struct Curl_URL CURLU; @@ -114,10 +115,10 @@ CURL_EXTERN CURLU *curl_url(void); /* * curl_url_cleanup() frees the CURLU handle and related resources used for - * the URL parsing. It will not free strings previously returned with the URL + * the URL parsing. It does not free strings previously returned with the URL * API. */ -CURL_EXTERN void curl_url_cleanup(CURLU *handle); +CURL_EXTERN void curl_url_cleanup(CURLU *u); /* * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new @@ -130,15 +131,15 @@ CURL_EXTERN CURLU *curl_url_dup(const CURLU *in); * handle. Returns error code. The returned pointer MUST be freed with * curl_free() afterwards. */ -CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags); /* * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns - * error code. The passed in string will be copied. Passing a NULL instead of + * error code. The passed in string is copied. Passing a NULL instead of * a part string, clears that part. */ -CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags); /* @@ -146,7 +147,7 @@ CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, * readable error string. This is useful for printing meaningful error * messages. */ -CURL_EXTERN const char *curl_url_strerror(CURLUcode); +CURL_EXTERN const char *curl_url_strerror(CURLUcode error); #ifdef __cplusplus } /* end of extern "C" */ diff --git a/curl/websockets.h b/curl/websockets.h index 6ef6a2b..bf93715 100644 --- a/curl/websockets.h +++ b/curl/websockets.h @@ -24,7 +24,7 @@ * ***************************************************************************/ -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -37,12 +37,12 @@ struct curl_ws_frame { }; /* flag bits */ -#define CURLWS_TEXT (1<<0) -#define CURLWS_BINARY (1<<1) -#define CURLWS_CONT (1<<2) -#define CURLWS_CLOSE (1<<3) -#define CURLWS_PING (1<<4) -#define CURLWS_OFFSET (1<<5) +#define CURLWS_TEXT (1 << 0) +#define CURLWS_BINARY (1 << 1) +#define CURLWS_CONT (1 << 2) +#define CURLWS_CLOSE (1 << 3) +#define CURLWS_PING (1 << 4) +#define CURLWS_OFFSET (1 << 5) /* * NAME curl_ws_recv() @@ -57,7 +57,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, const struct curl_ws_frame **metap); /* flags for curl_ws_send() */ -#define CURLWS_PONG (1<<6) +#define CURLWS_PONG (1 << 6) /* * NAME curl_ws_send() @@ -67,17 +67,31 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, * Sends data over the websocket connection. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags); +/* + * NAME curl_ws_start_frame() + * + * DESCRIPTION + * + * Buffers a websocket frame header with the given flags and length. + * Errors when a previous frame is not complete, e.g. not all its + * payload has been added. + */ +CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, + unsigned int flags, + curl_off_t frame_len); + /* bits for the CURLOPT_WS_OPTIONS bitmask: */ -#define CURLWS_RAW_MODE (1<<0) +#define CURLWS_RAW_MODE (1L << 0) +#define CURLWS_NOAUTOPONG (1L << 1) CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); -#ifdef __cplusplus +#ifdef __cplusplus } #endif diff --git a/curl/zlib.lib b/curl/zlib.lib index fb62a16..e977635 100644 Binary files a/curl/zlib.lib and b/curl/zlib.lib differ diff --git a/library.vcxproj b/library.vcxproj index a866126..70fc7bb 100644 --- a/library.vcxproj +++ b/library.vcxproj @@ -152,6 +152,7 @@ MultiThreadedDLL None %(AdditionalIncludeDirectories) + /guard:ehcont %(AdditionalOptions) diff --git a/libsodium/libsodium.lib b/libsodium/libsodium.lib index 4363627..1b7cc99 100644 Binary files a/libsodium/libsodium.lib and b/libsodium/libsodium.lib differ diff --git a/libsodium/sodium/crypto_core_ed25519.h b/libsodium/sodium/crypto_core_ed25519.h index dcc46a6..8f2eeee 100644 --- a/libsodium/sodium/crypto_core_ed25519.h +++ b/libsodium/sodium/crypto_core_ed25519.h @@ -42,6 +42,10 @@ int crypto_core_ed25519_sub(unsigned char *r, const unsigned char *p, const unsigned char *q) __attribute__ ((nonnull)); +/* + * Removal notice: this function is undocumented and has been marked as + * deprecated since 2019. It will be removed. Bindings should not refer to it. + */ SODIUM_EXPORT int crypto_core_ed25519_from_uniform(unsigned char *p, const unsigned char *r) __attribute__ ((nonnull)); diff --git a/libsodium/sodium/crypto_hash_sha256.h b/libsodium/sodium/crypto_hash_sha256.h index c47982a..f8311b2 100644 --- a/libsodium/sodium/crypto_hash_sha256.h +++ b/libsodium/sodium/crypto_hash_sha256.h @@ -2,7 +2,7 @@ #define crypto_hash_sha256_H /* - * WARNING: Unless you absolutely need to use SHA256 for interoperability, + * WARNING: Unless you absolutely need to use SHA256 for interoperability * purposes, you might want to consider crypto_generichash() instead. * Unlike SHA256, crypto_generichash() is not vulnerable to length * extension attacks. diff --git a/libsodium/sodium/crypto_hash_sha512.h b/libsodium/sodium/crypto_hash_sha512.h index 5b690fb..1628901 100644 --- a/libsodium/sodium/crypto_hash_sha512.h +++ b/libsodium/sodium/crypto_hash_sha512.h @@ -2,7 +2,7 @@ #define crypto_hash_sha512_H /* - * WARNING: Unless you absolutely need to use SHA512 for interoperability, + * WARNING: Unless you absolutely need to use SHA512 for interoperability * purposes, you might want to consider crypto_generichash() instead. * Unlike SHA512, crypto_generichash() is not vulnerable to length * extension attacks. diff --git a/libsodium/sodium/crypto_stream.h b/libsodium/sodium/crypto_stream.h index 88dab5f..9f3b57f 100644 --- a/libsodium/sodium/crypto_stream.h +++ b/libsodium/sodium/crypto_stream.h @@ -46,7 +46,7 @@ SODIUM_EXPORT int crypto_stream_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT void crypto_stream_keygen(unsigned char k[crypto_stream_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_chacha20.h b/libsodium/sodium/crypto_stream_chacha20.h index 4088975..cb034f3 100644 --- a/libsodium/sodium/crypto_stream_chacha20.h +++ b/libsodium/sodium/crypto_stream_chacha20.h @@ -43,14 +43,14 @@ SODIUM_EXPORT int crypto_stream_chacha20_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT int crypto_stream_chacha20_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint64_t ic, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 6))); SODIUM_EXPORT void crypto_stream_chacha20_keygen(unsigned char k[crypto_stream_chacha20_KEYBYTES]) @@ -80,14 +80,14 @@ SODIUM_EXPORT int crypto_stream_chacha20_ietf_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT int crypto_stream_chacha20_ietf_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint32_t ic, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 6))); SODIUM_EXPORT void crypto_stream_chacha20_ietf_keygen(unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_salsa20.h b/libsodium/sodium/crypto_stream_salsa20.h index 45b3b3e..27cc007 100644 --- a/libsodium/sodium/crypto_stream_salsa20.h +++ b/libsodium/sodium/crypto_stream_salsa20.h @@ -41,14 +41,14 @@ SODIUM_EXPORT int crypto_stream_salsa20_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT int crypto_stream_salsa20_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint64_t ic, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 6))); SODIUM_EXPORT void crypto_stream_salsa20_keygen(unsigned char k[crypto_stream_salsa20_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_salsa2012.h b/libsodium/sodium/crypto_stream_salsa2012.h index 6c5d303..f1ec382 100644 --- a/libsodium/sodium/crypto_stream_salsa2012.h +++ b/libsodium/sodium/crypto_stream_salsa2012.h @@ -40,7 +40,7 @@ SODIUM_EXPORT int crypto_stream_salsa2012_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT void crypto_stream_salsa2012_keygen(unsigned char k[crypto_stream_salsa2012_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_salsa208.h b/libsodium/sodium/crypto_stream_salsa208.h index d574f30..a716954 100644 --- a/libsodium/sodium/crypto_stream_salsa208.h +++ b/libsodium/sodium/crypto_stream_salsa208.h @@ -43,7 +43,7 @@ SODIUM_EXPORT int crypto_stream_salsa208_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((deprecated)) __attribute__ ((nonnull)); + __attribute__ ((deprecated)) __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT void crypto_stream_salsa208_keygen(unsigned char k[crypto_stream_salsa208_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_xchacha20.h b/libsodium/sodium/crypto_stream_xchacha20.h index c4002db..06f9908 100644 --- a/libsodium/sodium/crypto_stream_xchacha20.h +++ b/libsodium/sodium/crypto_stream_xchacha20.h @@ -41,14 +41,14 @@ SODIUM_EXPORT int crypto_stream_xchacha20_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT int crypto_stream_xchacha20_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint64_t ic, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 6))); SODIUM_EXPORT void crypto_stream_xchacha20_keygen(unsigned char k[crypto_stream_xchacha20_KEYBYTES]) diff --git a/libsodium/sodium/crypto_stream_xsalsa20.h b/libsodium/sodium/crypto_stream_xsalsa20.h index 20034e3..71d1675 100644 --- a/libsodium/sodium/crypto_stream_xsalsa20.h +++ b/libsodium/sodium/crypto_stream_xsalsa20.h @@ -41,14 +41,14 @@ SODIUM_EXPORT int crypto_stream_xsalsa20_xor(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 5))); SODIUM_EXPORT int crypto_stream_xsalsa20_xor_ic(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, uint64_t ic, const unsigned char *k) - __attribute__ ((nonnull)); + __attribute__ ((nonnull(1, 4, 6))); SODIUM_EXPORT void crypto_stream_xsalsa20_keygen(unsigned char k[crypto_stream_xsalsa20_KEYBYTES]) diff --git a/libsodium/sodium/utils.h b/libsodium/sodium/utils.h index f9b3697..0fd368a 100644 --- a/libsodium/sodium/utils.h +++ b/libsodium/sodium/utils.h @@ -109,33 +109,33 @@ int sodium_munlock(void * const addr, const size_t len) * allocation functions. * * They return a pointer to a region filled with 0xd0 bytes, immediately - * followed by a guard page. - * As a result, accessing a single byte after the requested allocation size - * will intentionally trigger a segmentation fault. + * followed by a guard page. As a result, accessing a single byte after the + * requested allocation size will intentionally trigger a segmentation fault. * * A canary and an additional guard page placed before the beginning of the * region may also kill the process if a buffer underflow is detected. * * The memory layout is: * [unprotected region size (read only)][guard page (no access)][unprotected pages (read/write)][guard page (no access)] - * With the layout of the unprotected pages being: + * + * The layout of the unprotected pages is: * [optional padding][16-bytes canary][user region] * - * However: - * - These functions are significantly slower than standard functions - * - Each allocation requires 3 or 4 additional pages + * Important limitations: + * - These functions are significantly slower than standard allocation functions. + * - Each allocation requires 3 or 4 additional pages. * - The returned address will not be aligned if the allocation size is not * a multiple of the required alignment. For this reason, these functions - * are designed to store data, such as secret keys and messages. + * are designed to store data such as secret keys and messages. * * sodium_malloc() can be used to allocate any libsodium data structure. * * The crypto_generichash_state structure is packed and its length is - * either 357 or 361 bytes. For this reason, when using sodium_malloc() to - * allocate a crypto_generichash_state structure, padding must be added in - * order to ensure proper alignment. crypto_generichash_statebytes() - * returns the rounded up structure size, and should be preferred to sizeof(): - * state = sodium_malloc(crypto_generichash_statebytes()); + * either 357 or 361 bytes. When using sodium_malloc() to allocate a + * crypto_generichash_state structure, padding must be added to ensure + * proper alignment. Use crypto_generichash_statebytes() rather than sizeof(): + * + * state = sodium_malloc(crypto_generichash_statebytes()); */ SODIUM_EXPORT diff --git a/xorstr.hpp b/xorstr.hpp index 19a4461..3accc03 100644 --- a/xorstr.hpp +++ b/xorstr.hpp @@ -17,7 +17,14 @@ namespace #define ALWAYS_INLINE __attribute__((always_inline)) #endif -template +constexpr size_t keyauth_xor_build_key(const char* time) +{ + return static_cast((time[7] - '0') + (time[6] - '0') * 10 + + (time[4] - '0') * 60 + (time[3] - '0') * 600 + + (time[1] - '0') * 3600 + (time[0] - '0') * 36000); +} + +template class _Basic_XorStr { using value_type = typename _string_type::value_type; @@ -39,9 +46,10 @@ class _Basic_XorStr inline auto str() const { - decrypt(); - - return _string_type(data, data + _length_minus_one); + _string_type result(_length_minus_one, value_type{}); + for (size_t t = 0; t < _length_minus_one; ++t) + result[t] = encrypted ? crypt(data[t], t) : data[t]; + return result; } inline operator _string_type() const @@ -58,14 +66,7 @@ class _Basic_XorStr } - static constexpr auto XOR_KEY = static_cast( - const_atoi(__TIME__[7]) + - const_atoi(__TIME__[6]) * 10 + - const_atoi(__TIME__[4]) * 60 + - const_atoi(__TIME__[3]) * 600 + - const_atoi(__TIME__[1]) * 3600 + - const_atoi(__TIME__[0]) * 36000 - ); + static constexpr auto XOR_KEY = static_cast(_key); static ALWAYS_INLINE constexpr auto crypt(value_type c, size_t i) { @@ -88,70 +89,70 @@ class _Basic_XorStr mutable bool encrypted; }; //--------------------------------------------------------------------------- -template -using XorStrA = _Basic_XorStr; -template -using XorStrW = _Basic_XorStr; -template -using XorStrU16 = _Basic_XorStr; -template -using XorStrU32 = _Basic_XorStr; +template +using XorStrA = _Basic_XorStr; +template +using XorStrW = _Basic_XorStr; +template +using XorStrU16 = _Basic_XorStr; +template +using XorStrU32 = _Basic_XorStr; //--------------------------------------------------------------------------- -template -inline auto operator==(const _Basic_XorStr<_string_type, _length>& lhs, const _Basic_XorStr<_string_type, _length2>& rhs) +template +inline auto operator==(const _Basic_XorStr<_string_type, _length, _key>& lhs, const _Basic_XorStr<_string_type, _length2, _key2>& rhs) { static_assert(_length == _length2, "XorStr== different length"); return _length == _length2 && lhs.str() == rhs.str(); } //--------------------------------------------------------------------------- -template -inline auto operator==(const _string_type& lhs, const _Basic_XorStr<_string_type, _length>& rhs) +template +inline auto operator==(const _string_type& lhs, const _Basic_XorStr<_string_type, _length, _key>& rhs) { - return lhs.size() == _length && lhs == rhs.str(); + return lhs.size() == _length - 1 && lhs == rhs.str(); } //--------------------------------------------------------------------------- -template -inline auto& operator<<(_stream_type& lhs, const _Basic_XorStr<_string_type, _length>& rhs) +template +inline auto& operator<<(_stream_type& lhs, const _Basic_XorStr<_string_type, _length, _key>& rhs) { lhs << rhs.c_str(); return lhs; } //--------------------------------------------------------------------------- -template -inline auto operator+(const _Basic_XorStr<_string_type, _length>& lhs, const _Basic_XorStr<_string_type, _length2>& rhs) +template +inline auto operator+(const _Basic_XorStr<_string_type, _length, _key>& lhs, const _Basic_XorStr<_string_type, _length2, _key2>& rhs) { return lhs.str() + rhs.str(); } //--------------------------------------------------------------------------- -template -inline auto operator+(const _string_type& lhs, const _Basic_XorStr<_string_type, _length>& rhs) +template +inline auto operator+(const _string_type& lhs, const _Basic_XorStr<_string_type, _length, _key>& rhs) { return lhs + rhs.str(); } //--------------------------------------------------------------------------- -template +template constexpr ALWAYS_INLINE auto XorStr(char const (&str)[_length]) { - return XorStrA<_length>(str); + return XorStrA<_length, _key>(str); } //--------------------------------------------------------------------------- -template +template constexpr ALWAYS_INLINE auto XorStr(wchar_t const (&str)[_length]) { - return XorStrW<_length>(str); + return XorStrW<_length, _key>(str); } //--------------------------------------------------------------------------- -template +template constexpr ALWAYS_INLINE auto XorStr(char16_t const (&str)[_length]) { - return XorStrU16<_length>(str); + return XorStrU16<_length, _key>(str); } //--------------------------------------------------------------------------- -template +template constexpr ALWAYS_INLINE auto XorStr(char32_t const (&str)[_length]) { - return XorStrU32<_length>(str); + return XorStrU32<_length, _key>(str); } //--------------------------------------------------------------------------- \ No newline at end of file