• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25  /*
26   * IDN conversions
27   */
28 
29 #include "curl_setup.h"
30 #include "urldata.h"
31 #include "idn.h"
32 #include "sendf.h"
33 #include "curl_multibyte.h"
34 #include "warnless.h"
35 
36 #ifdef USE_LIBIDN2
37 #include <idn2.h>
38 
39 #if defined(WIN32) && defined(UNICODE)
40 #define IDN2_LOOKUP(name, host, flags)                                  \
41   idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
42 #else
43 #define IDN2_LOOKUP(name, host, flags)                          \
44   idn2_lookup_ul((const char *)name, (char **)host, flags)
45 #endif
46 #endif  /* USE_LIBIDN2 */
47 
48 /* The last 3 #include files should be in this order */
49 #include "curl_printf.h"
50 #include "curl_memory.h"
51 #include "memdebug.h"
52 
53 #ifdef USE_WIN32_IDN
54 /* using Windows kernel32 and normaliz libraries. */
55 
56 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
57 WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags,
58                                  const WCHAR *lpUnicodeCharStr,
59                                  int cchUnicodeChar,
60                                  WCHAR *lpASCIICharStr,
61                                  int cchASCIIChar);
62 WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags,
63                                    const WCHAR *lpASCIICharStr,
64                                    int cchASCIIChar,
65                                    WCHAR *lpUnicodeCharStr,
66                                    int cchUnicodeChar);
67 #endif
68 
69 #define IDN_MAX_LENGTH 255
70 
Curl_win32_idn_to_ascii(const char * in,char ** out)71 bool Curl_win32_idn_to_ascii(const char *in, char **out)
72 {
73   bool success = FALSE;
74 
75   wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
76   if(in_w) {
77     wchar_t punycode[IDN_MAX_LENGTH];
78     int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH);
79     curlx_unicodefree(in_w);
80     if(chars) {
81       char *mstr = curlx_convert_wchar_to_UTF8(punycode);
82       if(mstr) {
83         *out = strdup(mstr);
84         curlx_unicodefree(mstr);
85         if(*out)
86           success = TRUE;
87       }
88     }
89   }
90 
91   return success;
92 }
93 
94 #endif /* USE_WIN32_IDN */
95 
96 /*
97  * Helpers for IDNA conversions.
98  */
Curl_is_ASCII_name(const char * hostname)99 bool Curl_is_ASCII_name(const char *hostname)
100 {
101   /* get an UNSIGNED local version of the pointer */
102   const unsigned char *ch = (const unsigned char *)hostname;
103 
104   if(!hostname) /* bad input, consider it ASCII! */
105     return TRUE;
106 
107   while(*ch) {
108     if(*ch++ & 0x80)
109       return FALSE;
110   }
111   return TRUE;
112 }
113 
114 #ifdef USE_IDN
115 /*
116  * Curl_idn_decode() returns an allocated IDN decoded string if it was
117  * possible. NULL on error.
118  */
idn_decode(const char * input)119 static char *idn_decode(const char *input)
120 {
121   char *decoded = NULL;
122 #ifdef USE_LIBIDN2
123   if(idn2_check_version(IDN2_VERSION)) {
124     int flags = IDN2_NFC_INPUT
125 #if IDN2_VERSION_NUMBER >= 0x00140000
126       /* IDN2_NFC_INPUT: Normalize input string using normalization form C.
127          IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional
128          processing. */
129       | IDN2_NONTRANSITIONAL
130 #endif
131       ;
132     int rc = IDN2_LOOKUP(input, &decoded, flags);
133     if(rc != IDN2_OK)
134       /* fallback to TR46 Transitional mode for better IDNA2003
135          compatibility */
136       rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL);
137     if(rc != IDN2_OK)
138       decoded = NULL;
139   }
140 #elif defined(USE_WIN32_IDN)
141   if(!Curl_win32_idn_to_ascii(input, &decoded))
142     decoded = NULL;
143 #endif
144   return decoded;
145 }
146 
Curl_idn_decode(const char * input)147 char *Curl_idn_decode(const char *input)
148 {
149   char *d = idn_decode(input);
150 #ifdef USE_LIBIDN2
151   if(d) {
152     char *c = strdup(d);
153     idn2_free(d);
154     d = c;
155   }
156 #endif
157   return d;
158 }
159 
160 /*
161  * Frees data allocated by idnconvert_hostname()
162  */
Curl_free_idnconverted_hostname(struct hostname * host)163 void Curl_free_idnconverted_hostname(struct hostname *host)
164 {
165   if(host->encalloc) {
166     /* must be freed with idn2_free() if allocated by libidn */
167     Curl_idn_free(host->encalloc);
168     host->encalloc = NULL;
169   }
170 }
171 
172 #endif /* USE_IDN */
173 
174 /*
175  * Perform any necessary IDN conversion of hostname
176  */
Curl_idnconvert_hostname(struct hostname * host)177 CURLcode Curl_idnconvert_hostname(struct hostname *host)
178 {
179   /* set the name we use to display the host name */
180   host->dispname = host->name;
181 
182 #ifdef USE_IDN
183   /* Check name for non-ASCII and convert hostname if we can */
184   if(!Curl_is_ASCII_name(host->name)) {
185     char *decoded = idn_decode(host->name);
186     if(decoded) {
187       if(!*decoded) {
188         /* zero length is a bad host name */
189         Curl_idn_free(decoded);
190         return CURLE_URL_MALFORMAT;
191       }
192       /* successful */
193       host->encalloc = decoded;
194       /* change the name pointer to point to the encoded hostname */
195       host->name = host->encalloc;
196     }
197     else
198       return CURLE_URL_MALFORMAT;
199   }
200 #endif
201   return CURLE_OK;
202 }
203