From: MonkeybreadSoftware Date: Sun, 31 Mar 2024 09:55:27 +0000 (+0200) Subject: idn: add native AppleIDN (icucore) support for macOS/iOS X-Git-Tag: curl-8_8_0~210 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=add22feeef07858307;p=thirdparty%2Fcurl.git idn: add native AppleIDN (icucore) support for macOS/iOS I implemented the IDN functions for macOS and iOS using Unicode libraries coming with macOS and iOS. Builds and runs here on macOS 14.2.1. Also verified to load and run on older macOS version 10.13. Build requires macOS SDK 13 or equivalent. Set `-DUSE_APPLE_IDN=ON` CMake option to enable it. With autotools and other build tools, set these manual options: ``` CPPFLAGS=-DUSE_APPLE_IDN LIBS=-licucore ``` Completes TODO 1.6. TODO: add autotools option and feature-detection. Refs: #5330 #5371 Co-authored-by: Viktor Szakats Closes #13246 --- diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5766b18363..a3958820c9 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -239,7 +239,7 @@ jobs: - uses: actions/checkout@v4 - - run: cmake -B build -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON ${{ matrix.build.generate }} + - run: cmake -B build -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DUSE_APPLE_IDN=ON ${{ matrix.build.generate }} name: 'cmake generate' - run: cmake --build build --parallel 3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cc8a9d2e2..51b4aa3654 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -899,6 +899,21 @@ if(WIN32) endif() endif() +if(APPLE) + option(USE_APPLE_IDN "Use Apple built-in IDN support" OFF) + if(USE_APPLE_IDN) + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES "icucore") + check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN) + cmake_pop_check_state() + if(HAVE_APPLE_IDN) + list(APPEND CURL_LIBS "icucore") + else() + set(USE_APPLE_IDN OFF) + endif() + endif() +endif() + #libpsl option(CURL_USE_LIBPSL "Use libPSL" ON) mark_as_advanced(CURL_USE_LIBPSL) @@ -1600,7 +1615,7 @@ if(NOT CURL_DISABLE_INSTALL) _add_if("brotli" HAVE_BROTLI) _add_if("zstd" HAVE_ZSTD) _add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) - _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN) + _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN OR USE_APPLE_IDN) _add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) _add_if("SSPI" USE_WINDOWS_SSPI) diff --git a/docs/TODO b/docs/TODO index 159e2e8753..e5bf092433 100644 --- a/docs/TODO +++ b/docs/TODO @@ -22,7 +22,6 @@ 1.3 struct lifreq 1.4 Better and more sharing 1.5 get rid of PATH_MAX - 1.6 native IDN support on macOS 1.8 CURLOPT_RESOLVE for any port number 1.9 Cache negative name resolves 1.10 auto-detect proxy @@ -251,16 +250,6 @@ there we need libssh2 to properly tell us when we pass in a too small buffer and its current API (as of libssh2 1.2.7) does not. -1.6 native IDN support on macOS - - On recent macOS versions, the getaddrinfo() function itself has built-in IDN - support. By setting the AI_CANONNAME flag, the function will return the - encoded name in the ai_canonname struct field in the returned information. - This could be used by curl on macOS when built without a separate IDN library - and an IDN host name is used in a URL. - - See initial work in https://github.com/curl/curl/pull/5371 - 1.8 CURLOPT_RESOLVE for any port number This option allows applications to set a replacement IP address for a given diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index 11b0cb54fd..5d394675d2 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -794,6 +794,9 @@ ${SIZEOF_TIME_T_CODE} /* to enable Windows IDN */ #cmakedefine USE_WIN32_IDN 1 +/* to enable Apple IDN */ +#cmakedefine USE_APPLE_IDN 1 + /* Define to 1 to enable websocket support. */ #cmakedefine USE_WEBSOCKETS 1 diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 620680b54f..5dd94b3c79 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -649,13 +649,14 @@ /* ---------------------------------------------------------------- */ -#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) +#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && \ + !defined(USE_WIN32_IDN) && !defined(USE_APPLE_IDN) /* The lib and header are present */ #define USE_LIBIDN2 #endif -#if defined(USE_LIBIDN2) && defined(USE_WIN32_IDN) -#error "Both libidn2 and WinIDN are enabled, choose one." +#if defined(USE_LIBIDN2) && (defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)) +#error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one." #endif #define LIBIDN_REQUIRED_VERSION "0.4.1" diff --git a/lib/idn.c b/lib/idn.c index 3890b0b06b..c795672544 100644 --- a/lib/idn.c +++ b/lib/idn.c @@ -50,6 +50,63 @@ #include "curl_memory.h" #include "memdebug.h" +/* for macOS and iOS targets */ +#if defined(USE_APPLE_IDN) +#include + +static CURLcode mac_idn_to_ascii(const char *in, char **out) +{ + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); + if(U_FAILURE(err)) { + return CURLE_OUT_OF_MEMORY; + } + else { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[256] = {0}; + (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer, + sizeof(buffer), &info, &err); + uidna_close(idna); + if(U_FAILURE(err)) { + return CURLE_URL_MALFORMAT; + } + else { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } +} + +static CURLcode mac_ascii_to_idn(const char *in, char **out) +{ + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); + if(U_FAILURE(err)) { + return CURLE_OUT_OF_MEMORY; + } + else { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[256] = {0}; + (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, + sizeof(buffer), &info, &err); + uidna_close(idna); + if(U_FAILURE(err)) { + return CURLE_URL_MALFORMAT; + } + else { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } +} +#endif + #ifdef USE_WIN32_IDN /* using Windows kernel32 and normaliz libraries. */ @@ -181,6 +238,8 @@ static CURLcode idn_decode(const char *input, char **output) result = CURLE_NOT_BUILT_IN; #elif defined(USE_WIN32_IDN) result = win32_idn_to_ascii(input, &decoded); +#elif defined(USE_APPLE_IDN) + result = mac_idn_to_ascii(input, &decoded); #endif if(!result) *output = decoded; @@ -198,6 +257,10 @@ static CURLcode idn_encode(const char *puny, char **output) CURLcode result = win32_ascii_to_idn(puny, &enc); if(result) return result; +#elif defined(USE_APPLE_IDN) + CURLcode result = mac_ascii_to_idn(puny, &enc); + if(result) + return result; #endif *output = enc; return CURLE_OK; diff --git a/lib/idn.h b/lib/idn.h index e75124ef9f..2bdce8927f 100644 --- a/lib/idn.h +++ b/lib/idn.h @@ -26,7 +26,7 @@ bool Curl_is_ASCII_name(const char *hostname); CURLcode Curl_idnconvert_hostname(struct hostname *host); -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); CURLcode Curl_idn_decode(const char *input, char **output); diff --git a/lib/version.c b/lib/version.c index 257c1fedbf..66371923a7 100644 --- a/lib/version.c +++ b/lib/version.c @@ -209,6 +209,8 @@ char *curl_version(void) src[i++] = idn_version; #elif defined(USE_WIN32_IDN) src[i++] = (char *)"WinIDN"; +#elif defined(USE_APPLE_IDN) + src[i++] = (char *)"AppleIDN"; #endif #ifdef USE_LIBPSL @@ -475,7 +477,7 @@ static const struct feat features_table[] = { !defined(CURL_DISABLE_HTTP) FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY), #endif -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) FEATURE("IDN", idn_present, CURL_VERSION_IDN), #endif #ifdef USE_IPV6 diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 1509c76a7f..2f7c76a433 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -31,7 +31,7 @@ */ #include "test.h" -#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #define USE_IDN #endif