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/* Escape and unescape URL encoding in strings. The functions return a new 26 * allocated string or NULL if an error occurred. */ 27 28#include "curl_setup.h" 29 30#include <curl/curl.h> 31 32#include "urldata.h" 33#include "warnless.h" 34#include "escape.h" 35#include "strdup.h" 36/* The last 3 #include files should be in this order */ 37#include "curl_printf.h" 38#include "curl_memory.h" 39#include "memdebug.h" 40 41/* for ABI-compatibility with previous versions */ 42char *curl_escape(const char *string, int inlength) 43{ 44 return curl_easy_escape(NULL, string, inlength); 45} 46 47/* for ABI-compatibility with previous versions */ 48char *curl_unescape(const char *string, int length) 49{ 50 return curl_easy_unescape(NULL, string, length, NULL); 51} 52 53/* Escapes for URL the given unescaped string of given length. 54 * 'data' is ignored since 7.82.0. 55 */ 56char *curl_easy_escape(struct Curl_easy *data, const char *string, 57 int inlength) 58{ 59 size_t length; 60 struct dynbuf d; 61 (void)data; 62 63 if(inlength < 0) 64 return NULL; 65 66 Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3); 67 68 length = (inlength?(size_t)inlength:strlen(string)); 69 if(!length) 70 return strdup(""); 71 72 while(length--) { 73 unsigned char in = *string++; /* treat the characters unsigned */ 74 75 if(ISUNRESERVED(in)) { 76 /* append this */ 77 if(Curl_dyn_addn(&d, &in, 1)) 78 return NULL; 79 } 80 else { 81 /* encode it */ 82 const char hex[] = "0123456789ABCDEF"; 83 char out[3]={'%'}; 84 out[1] = hex[in>>4]; 85 out[2] = hex[in & 0xf]; 86 if(Curl_dyn_addn(&d, out, 3)) 87 return NULL; 88 } 89 } 90 91 return Curl_dyn_ptr(&d); 92} 93 94static const unsigned char hextable[] = { 95 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ 96 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ 97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ 98 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */ 99}; 100 101/* the input is a single hex digit */ 102#define onehex2dec(x) hextable[x - '0'] 103 104/* 105 * Curl_urldecode() URL decodes the given string. 106 * 107 * Returns a pointer to a malloced string in *ostring with length given in 108 * *olen. If length == 0, the length is assumed to be strlen(string). 109 * 110 * ctrl options: 111 * - REJECT_NADA: accept everything 112 * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in 113 * the data 114 * - REJECT_ZERO: rejects decoded zero bytes 115 * 116 * The values for the enum starts at 2, to make the assert detect legacy 117 * invokes that used TRUE/FALSE (0 and 1). 118 */ 119 120CURLcode Curl_urldecode(const char *string, size_t length, 121 char **ostring, size_t *olen, 122 enum urlreject ctrl) 123{ 124 size_t alloc; 125 char *ns; 126 127 DEBUGASSERT(string); 128 DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ 129 130 alloc = (length?length:strlen(string)); 131 ns = malloc(alloc + 1); 132 133 if(!ns) 134 return CURLE_OUT_OF_MEMORY; 135 136 /* store output string */ 137 *ostring = ns; 138 139 while(alloc) { 140 unsigned char in = *string; 141 if(('%' == in) && (alloc > 2) && 142 ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { 143 /* this is two hexadecimal digits following a '%' */ 144 in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]); 145 146 string += 3; 147 alloc -= 3; 148 } 149 else { 150 string++; 151 alloc--; 152 } 153 154 if(((ctrl == REJECT_CTRL) && (in < 0x20)) || 155 ((ctrl == REJECT_ZERO) && (in == 0))) { 156 Curl_safefree(*ostring); 157 return CURLE_URL_MALFORMAT; 158 } 159 160 *ns++ = in; 161 } 162 *ns = 0; /* terminate it */ 163 164 if(olen) 165 /* store output size */ 166 *olen = ns - *ostring; 167 168 return CURLE_OK; 169} 170 171/* 172 * Unescapes the given URL escaped string of given length. Returns a 173 * pointer to a malloced string with length given in *olen. 174 * If length == 0, the length is assumed to be strlen(string). 175 * If olen == NULL, no output length is stored. 176 * 'data' is ignored since 7.82.0. 177 */ 178char *curl_easy_unescape(struct Curl_easy *data, const char *string, 179 int length, int *olen) 180{ 181 char *str = NULL; 182 (void)data; 183 if(length >= 0) { 184 size_t inputlen = (size_t)length; 185 size_t outputlen; 186 CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, 187 REJECT_NADA); 188 if(res) 189 return NULL; 190 191 if(olen) { 192 if(outputlen <= (size_t) INT_MAX) 193 *olen = curlx_uztosi(outputlen); 194 else 195 /* too large to return in an int, fail! */ 196 Curl_safefree(str); 197 } 198 } 199 return str; 200} 201 202/* For operating systems/environments that use different malloc/free 203 systems for the app and for this library, we provide a free that uses 204 the library's memory system */ 205void curl_free(void *p) 206{ 207 free(p); 208} 209 210/* 211 * Curl_hexencode() 212 * 213 * Converts binary input to lowercase hex-encoded ASCII output. 214 * Null-terminated. 215 */ 216void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ 217 unsigned char *out, size_t olen) /* output buffer size */ 218{ 219 const char *hex = "0123456789abcdef"; 220 DEBUGASSERT(src && len && (olen >= 3)); 221 if(src && len && (olen >= 3)) { 222 while(len-- && (olen >= 3)) { 223 /* clang-tidy warns on this line without this comment: */ 224 /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ 225 *out++ = hex[(*src & 0xF0)>>4]; 226 *out++ = hex[*src & 0x0F]; 227 ++src; 228 olen -= 2; 229 } 230 *out = 0; 231 } 232 else if(olen) 233 *out = 0; 234} 235