123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
- /*
- * IDN conversions
- */
- #include "curl_setup.h"
- #include "urldata.h"
- #include "idn.h"
- #include "sendf.h"
- #include "curl_multibyte.h"
- #include "warnless.h"
- #ifdef USE_LIBIDN2
- #include <idn2.h>
- #if defined(_WIN32) && defined(UNICODE)
- #define IDN2_LOOKUP(name, host, flags) \
- idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
- #else
- #define IDN2_LOOKUP(name, host, flags) \
- idn2_lookup_ul((const char *)name, (char **)host, flags)
- #endif
- #endif /* USE_LIBIDN2 */
- /* The last 3 #include files should be in this order */
- #include "curl_printf.h"
- #include "curl_memory.h"
- #include "memdebug.h"
- /* for macOS and iOS targets */
- #if defined(USE_APPLE_IDN)
- #include <unicode/uidna.h>
- #define MAX_HOST_LENGTH 512
- static CURLcode mac_idn_to_ascii(const char *in, char **out)
- {
- size_t inlen = strlen(in);
- if(inlen < MAX_HOST_LENGTH) {
- UErrorCode err = U_ZERO_ERROR;
- UIDNA* idna = uidna_openUTS46(
- UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err);
- if(!U_FAILURE(err)) {
- UIDNAInfo info = UIDNA_INFO_INITIALIZER;
- char buffer[MAX_HOST_LENGTH] = {0};
- (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer,
- sizeof(buffer) - 1, &info, &err);
- uidna_close(idna);
- if(!U_FAILURE(err)) {
- *out = strdup(buffer);
- if(*out)
- return CURLE_OK;
- else
- return CURLE_OUT_OF_MEMORY;
- }
- }
- }
- return CURLE_URL_MALFORMAT;
- }
- static CURLcode mac_ascii_to_idn(const char *in, char **out)
- {
- size_t inlen = strlen(in);
- if(inlen < MAX_HOST_LENGTH) {
- UErrorCode err = U_ZERO_ERROR;
- UIDNA* idna = uidna_openUTS46(
- UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_UNICODE, &err);
- if(!U_FAILURE(err)) {
- UIDNAInfo info = UIDNA_INFO_INITIALIZER;
- char buffer[MAX_HOST_LENGTH] = {0};
- (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer,
- sizeof(buffer) - 1, &info, &err);
- uidna_close(idna);
- if(!U_FAILURE(err)) {
- *out = strdup(buffer);
- if(*out)
- return CURLE_OK;
- else
- return CURLE_OUT_OF_MEMORY;
- }
- }
- }
- return CURLE_URL_MALFORMAT;
- }
- #endif
- #ifdef USE_WIN32_IDN
- /* using Windows kernel32 and normaliz libraries. */
- #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
- WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags,
- const WCHAR *lpUnicodeCharStr,
- int cchUnicodeChar,
- WCHAR *lpASCIICharStr,
- int cchASCIIChar);
- WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags,
- const WCHAR *lpASCIICharStr,
- int cchASCIIChar,
- WCHAR *lpUnicodeCharStr,
- int cchUnicodeChar);
- #endif
- #define IDN_MAX_LENGTH 255
- static CURLcode win32_idn_to_ascii(const char *in, char **out)
- {
- wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
- *out = NULL;
- if(in_w) {
- wchar_t punycode[IDN_MAX_LENGTH];
- int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode,
- IDN_MAX_LENGTH);
- curlx_unicodefree(in_w);
- if(chars) {
- char *mstr = curlx_convert_wchar_to_UTF8(punycode);
- if(mstr) {
- *out = strdup(mstr);
- curlx_unicodefree(mstr);
- if(!*out)
- return CURLE_OUT_OF_MEMORY;
- }
- else
- return CURLE_OUT_OF_MEMORY;
- }
- else
- return CURLE_URL_MALFORMAT;
- }
- else
- return CURLE_URL_MALFORMAT;
- return CURLE_OK;
- }
- static CURLcode win32_ascii_to_idn(const char *in, char **output)
- {
- char *out = NULL;
- wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
- if(in_w) {
- WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */
- int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn,
- IDN_MAX_LENGTH);
- if(chars) {
- /* 'chars' is "the number of characters retrieved" */
- char *mstr = curlx_convert_wchar_to_UTF8(idn);
- if(mstr) {
- out = strdup(mstr);
- curlx_unicodefree(mstr);
- if(!out)
- return CURLE_OUT_OF_MEMORY;
- }
- }
- else
- return CURLE_URL_MALFORMAT;
- }
- else
- return CURLE_URL_MALFORMAT;
- *output = out;
- return CURLE_OK;
- }
- #endif /* USE_WIN32_IDN */
- /*
- * Helpers for IDNA conversions.
- */
- bool Curl_is_ASCII_name(const char *hostname)
- {
- /* get an UNSIGNED local version of the pointer */
- const unsigned char *ch = (const unsigned char *)hostname;
- if(!hostname) /* bad input, consider it ASCII! */
- return TRUE;
- while(*ch) {
- if(*ch++ & 0x80)
- return FALSE;
- }
- return TRUE;
- }
- #ifdef USE_IDN
- /*
- * Curl_idn_decode() returns an allocated IDN decoded string if it was
- * possible. NULL on error.
- *
- * CURLE_URL_MALFORMAT - the hostname could not be converted
- * CURLE_OUT_OF_MEMORY - memory problem
- *
- */
- static CURLcode idn_decode(const char *input, char **output)
- {
- char *decoded = NULL;
- CURLcode result = CURLE_OK;
- #ifdef USE_LIBIDN2
- if(idn2_check_version(IDN2_VERSION)) {
- int flags = IDN2_NFC_INPUT
- #if IDN2_VERSION_NUMBER >= 0x00140000
- /* IDN2_NFC_INPUT: Normalize input string using normalization form C.
- IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional
- processing. */
- | IDN2_NONTRANSITIONAL
- #endif
- ;
- int rc = IDN2_LOOKUP(input, &decoded, flags);
- if(rc != IDN2_OK)
- /* fallback to TR46 Transitional mode for better IDNA2003
- compatibility */
- rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL);
- if(rc != IDN2_OK)
- result = CURLE_URL_MALFORMAT;
- }
- else
- /* a too old libidn2 version */
- 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;
- return result;
- }
- static CURLcode idn_encode(const char *puny, char **output)
- {
- char *enc = NULL;
- #ifdef USE_LIBIDN2
- int rc = idn2_to_unicode_8z8z(puny, &enc, 0);
- if(rc != IDNA_SUCCESS)
- return rc == IDNA_MALLOC_ERROR ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT;
- #elif defined(USE_WIN32_IDN)
- 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;
- }
- CURLcode Curl_idn_decode(const char *input, char **output)
- {
- char *d = NULL;
- CURLcode result = idn_decode(input, &d);
- #ifdef USE_LIBIDN2
- if(!result) {
- char *c = strdup(d);
- idn2_free(d);
- if(c)
- d = c;
- else
- result = CURLE_OUT_OF_MEMORY;
- }
- #endif
- if(!result)
- *output = d;
- return result;
- }
- CURLcode Curl_idn_encode(const char *puny, char **output)
- {
- char *d = NULL;
- CURLcode result = idn_encode(puny, &d);
- #ifdef USE_LIBIDN2
- if(!result) {
- char *c = strdup(d);
- idn2_free(d);
- if(c)
- d = c;
- else
- result = CURLE_OUT_OF_MEMORY;
- }
- #endif
- if(!result)
- *output = d;
- return result;
- }
- /*
- * Frees data allocated by idnconvert_hostname()
- */
- void Curl_free_idnconverted_hostname(struct hostname *host)
- {
- Curl_safefree(host->encalloc);
- }
- #endif /* USE_IDN */
- /*
- * Perform any necessary IDN conversion of hostname
- */
- CURLcode Curl_idnconvert_hostname(struct hostname *host)
- {
- /* set the name we use to display the hostname */
- host->dispname = host->name;
- #ifdef USE_IDN
- /* Check name for non-ASCII and convert hostname if we can */
- if(!Curl_is_ASCII_name(host->name)) {
- char *decoded;
- CURLcode result = Curl_idn_decode(host->name, &decoded);
- if(result)
- return result;
- /* successful */
- host->name = host->encalloc = decoded;
- }
- #endif
- return CURLE_OK;
- }
|