]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
idn: add native AppleIDN (icucore) support for macOS/iOS
authorMonkeybreadSoftware <support@monkeybreadsoftware.de>
Sun, 31 Mar 2024 09:55:27 +0000 (11:55 +0200)
committerViktor Szakats <commit@vsz.me>
Tue, 16 Apr 2024 22:24:09 +0000 (00:24 +0200)
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

.github/workflows/macos.yml
CMakeLists.txt
docs/TODO
lib/curl_config.h.cmake
lib/curl_setup.h
lib/idn.c
lib/idn.h
lib/version.c
tests/libtest/lib1560.c

index 5766b1836313415f190736a39ad9ee896f7098a3..a3958820c98039a1fc8176786fc39444c5bc4384 100644 (file)
@@ -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
index 4cc8a9d2e200aa4df49cf465d7c0e7542cde3483..51b4aa365437134cbc839f67435259ba0dc372b5 100644 (file)
@@ -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)
index 159e2e8753597c52b073ba8e0c241ffbc3ab7883..e5bf092433fb0c96f7132ae2f647c8c5eb89ca1b 100644 (file)
--- 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
  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
index 11b0cb54fdcafef175c1985dadd100097b8c2d5a..5d394675d2466373c1fe7fa154418830598455b8 100644 (file)
@@ -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
 
index 620680b54ff4f6a32a8ea17d9ad97dea0ae35139..5dd94b3c79a0d12920c76b51e98b8f95663cfa4e 100644 (file)
 
 /* ---------------------------------------------------------------- */
 
-#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"
index 3890b0b06b1cb7b7f7d18d1f1cd1835b72b47967..c795672544125ed5480c1f30f6feaae2daba6f32 100644 (file)
--- a/lib/idn.c
+++ b/lib/idn.c
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* for macOS and iOS targets */
+#if defined(USE_APPLE_IDN)
+#include <unicode/uidna.h>
+
+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;
index e75124ef9f66182abf977091496f4c8c8203057e..2bdce8927f9e9f4359b86698de488dad20d65811 100644 (file)
--- 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);
index 257c1fedbf17a7d2cc643ad4dabf2440f3b00db1..66371923a732577f80cc2ae3537c53fd5b55b912 100644 (file)
@@ -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
index 1509c76a7fd7d21b98df862de5596c7da79aa380..2f7c76a433250f0d83c324e30e76aa5fc497867b 100644 (file)
@@ -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