master
c 313 lines 8.36 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "../libnetdata.h"
4
5 // ----------------------------------------------------------------------------
6 // URL encode / decode
7 // code from: http://www.geekhideout.com/urlcode.shtml
8
9 /* Converts a hex character to its integer value */
10 char from_hex(char ch) {
11 return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
12 }
13
14 /* Converts an integer value to its hex character*/
15 char to_hex(char code) {
16 static char hex[] = "0123456789abcdef";
17 return hex[code & 15];
18 }
19
20 /* Returns an url-encoded version of str */
21 /* IMPORTANT: be sure to free() the returned string after use */
22 char *url_encode(const char *str) {
23 char *buf, *pbuf;
24
25 pbuf = buf = mallocz(strlen(str) * 3 + 1);
26
27 while (*str) {
28 if (isalnum((uint8_t)*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
29 *pbuf++ = *str;
30
31 else if (*str == ' ')
32 *pbuf++ = '+';
33
34 else{
35 *pbuf++ = '%';
36 *pbuf++ = to_hex((char)(*str >> 4));
37 *pbuf++ = to_hex((char)(*str & 15));
38 }
39
40 str++;
41 }
42 *pbuf = '\0';
43
44 pbuf = strdupz(buf);
45 freez(buf);
46 return pbuf;
47 }
48
49 /**
50 * Percentage escape decode
51 *
52 * Decode %XX character or return 0 if cannot
53 *
54 * @param s the string to decode
55 *
56 * @return The character decoded on success and 0 otherwise
57 */
58 char url_percent_escape_decode(const char *s) {
59 if(likely(s[1] && s[2]))
60 return (char)(from_hex(s[1]) << 4 | from_hex(s[2]));
61 return 0;
62 }
63
64 /**
65 * Get byte length
66 *
67 * This (utf8 string related) should be moved in separate file in future
68 *
69 * @param c is the utf8 character
70 * *
71 * @return It returns the length of the specific character.
72 */
73 char url_utf8_get_byte_length(char c) {
74 if(!IS_UTF8_BYTE(c))
75 return 1;
76
77 char length = 0;
78 while(likely(c & 0x80)) {
79 length++;
80 c <<= 1;
81 }
82 //4 byte is max size for UTF-8 char
83 //10XX XXXX is not valid character -> check length == 1
84 if(length > 4 || length == 1)
85 return -1;
86
87 return length;
88 }
89
90 /**
91 * Decode Multibyte UTF8
92 *
93 * Decode % encoded UTF-8 characters and copy them to *d
94 *
95 * @param s first address
96 * @param d
97 * @param d_end last address
98 *
99 * @return count of bytes written to *d
100 */
101 char url_decode_multibyte_utf8(const char *s, char *d, const char *d_end) {
102 char first_byte = url_percent_escape_decode(s);
103
104 if(unlikely(!first_byte || !IS_UTF8_STARTBYTE(first_byte)))
105 return 0;
106
107 char byte_length = url_utf8_get_byte_length(first_byte);
108
109 if(unlikely(byte_length <= 0 || d+byte_length >= d_end))
110 return 0;
111
112 char to_read = byte_length;
113 while(to_read > 0) {
114 char c = url_percent_escape_decode(s);
115
116 if(unlikely( !IS_UTF8_BYTE(c) ))
117 return 0;
118 if((to_read != byte_length) && IS_UTF8_STARTBYTE(c))
119 return 0;
120
121 *d++ = c;
122 s+=3;
123 to_read--;
124 }
125
126 return byte_length;
127 }
128
129 /*
130 * The utf8_check() function scans the '\0'-terminated string starting
131 * at s. It returns a pointer to the first byte of the first malformed
132 * or overlong UTF-8 sequence found, or NULL if the string contains
133 * only correct UTF-8. It also spots UTF-8 sequences that could cause
134 * trouble if converted to UTF-16, namely surrogate characters
135 * (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This
136 * routine is very likely to find a malformed sequence if the input
137 * uses any other encoding than UTF-8. It therefore can be used as a
138 * very effective heuristic for distinguishing between UTF-8 and other
139 * encodings.
140 *
141 * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
142 * License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
143 */
144 unsigned char *utf8_check(unsigned char *s)
145 {
146 while (*s)
147 {
148 if (*s < 0x80)
149 /* 0xxxxxxx */
150 s++;
151 else if ((s[0] & 0xe0) == 0xc0)
152 {
153 /* 110XXXXx 10xxxxxx */
154 if ((s[1] & 0xc0) != 0x80 ||
155 (s[0] & 0xfe) == 0xc0) /* overlong? */
156 return s;
157 else
158 s += 2;
159 }
160 else if ((s[0] & 0xf0) == 0xe0)
161 {
162 /* 1110XXXX 10Xxxxxx 10xxxxxx */
163 if ((s[1] & 0xc0) != 0x80 ||
164 (s[2] & 0xc0) != 0x80 ||
165 (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
166 (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
167 (s[0] == 0xef && s[1] == 0xbf &&
168 (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
169 return s;
170 else
171 s += 3;
172 }
173 else if ((s[0] & 0xf8) == 0xf0)
174 {
175 /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
176 if ((s[1] & 0xc0) != 0x80 ||
177 (s[2] & 0xc0) != 0x80 ||
178 (s[3] & 0xc0) != 0x80 ||
179 (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
180 (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
181 return s;
182 else
183 s += 4;
184 }
185 else
186 return s;
187 }
188
189 return NULL;
190 }
191
192 char *url_decode_r(char *to, const char *url, size_t size) {
193 const char *s = url; // source
194 char *d = to, // destination
195 *e = &to[size - 1]; // destination end
196
197 while(*s && d < e) {
198 if(unlikely(*s == '%')) {
199 char t = url_percent_escape_decode(s);
200 if(IS_UTF8_BYTE(t)) {
201 char bytes_written = url_decode_multibyte_utf8(s, d, e);
202 if(likely(bytes_written)){
203 d += bytes_written;
204 s += (bytes_written * 3)-1;
205 }
206 else {
207 goto fail_cleanup;
208 }
209 }
210 else if(likely(t) && isprint(t)) {
211 // avoid HTTP header injection
212 *d++ = t;
213 s += 2;
214 }
215 else
216 goto fail_cleanup;
217 }
218 else if(unlikely(*s == '+'))
219 *d++ = ' ';
220
221 else
222 *d++ = *s;
223
224 s++;
225 }
226
227 *d = '\0';
228
229 if(unlikely( utf8_check((unsigned char *)to) )) //NULL means success here
230 return NULL;
231
232 return to;
233
234 fail_cleanup:
235 *d = '\0';
236 return NULL;
237 }
238
239 inline bool
240 url_is_request_complete_and_extract_payload(const char *begin, const char *end, size_t length, BUFFER **post_payload) {
241 if (begin == end || length < 4)
242 return false;
243
244 if(likely(strncmp(begin, "GET ", 4)) == 0) {
245 return strstr(end - 4, "\r\n\r\n");
246 }
247 else if(unlikely(strncmp(begin, "POST ", 5) == 0 || strncmp(begin, "PUT ", 4) == 0)) {
248 const char *cl = strcasestr(begin, "Content-Length: ");
249 if(!cl) return false;
250 cl = &cl[16];
251
252 size_t content_length = str2ul(cl);
253
254 const char *payload = strstr(cl, "\r\n\r\n");
255 if(!payload) return false;
256 payload += 4;
257
258 size_t payload_length = length - (payload - begin);
259
260 if(payload_length == content_length) {
261 if(!*post_payload)
262 *post_payload = buffer_create(payload_length + 1, NULL);
263
264 buffer_contents_replace(*post_payload, payload, payload_length);
265
266 // parse the content type
267 const char *ct = strcasestr(begin, "Content-Type: ");
268 if(ct) {
269 ct = &ct[14];
270 while (*ct && isspace((uint8_t)*ct)) ct++;
271 const char *space = ct;
272 while (*space && !isspace((uint8_t)*space) && *space != ';') space++;
273 size_t ct_len = space - ct;
274
275 CLEAN_CHAR_P *ct_copy = mallocz(ct_len + 1);
276 memcpy(ct_copy, ct, ct_len);
277 ct_copy[ct_len] = '\0';
278
279 (*post_payload)->content_type = content_type_string2id(ct_copy);
280 }
281 else
282 (*post_payload)->content_type = CT_TEXT_PLAIN;
283
284 return true;
285 }
286
287 return false;
288 }
289 else {
290 return strstr(end - 4, "\r\n\r\n");
291 }
292 }
293
294 /**
295 * Find protocol
296 *
297 * Search for the string ' HTTP/' in the message given.
298 *
299 * @param s is the start of the user request.
300 * @return
301 */
302 inline char *url_find_protocol(char *s) {
303 while(*s) {
304 // find the next space
305 while (*s && *s != ' ') s++;
306
307 // is it SPACE + "HTTP/" ?
308 if(*s && !strncmp(s, " HTTP/", 6)) break;
309 else s++;
310 }
311
312 return s;
313 }