Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (net_http.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <ctype.h> | |
26 | ||
27 | #include <net/net_http.h> | |
28 | #include <net/net_compat.h> | |
29 | #include <net/net_socket.h> | |
30 | #ifdef HAVE_SSL | |
31 | #include <net/net_socket_ssl.h> | |
32 | #endif | |
33 | #include <compat/strl.h> | |
34 | #include <string/stdstring.h> | |
35 | #include <string.h> | |
36 | #include <retro_common_api.h> | |
37 | #include <retro_miscellaneous.h> | |
38 | ||
39 | enum | |
40 | { | |
41 | P_HEADER_TOP = 0, | |
42 | P_HEADER, | |
43 | P_BODY, | |
44 | P_BODY_CHUNKLEN, | |
45 | P_DONE, | |
46 | P_ERROR | |
47 | }; | |
48 | ||
49 | enum | |
50 | { | |
51 | T_FULL = 0, | |
52 | T_LEN, | |
53 | T_CHUNK | |
54 | }; | |
55 | ||
56 | struct http_socket_state_t | |
57 | { | |
58 | void *ssl_ctx; | |
59 | int fd; | |
60 | bool ssl; | |
61 | }; | |
62 | ||
63 | struct http_t | |
64 | { | |
65 | char *data; | |
66 | struct http_socket_state_t sock_state; /* ptr alignment */ | |
67 | size_t pos; | |
68 | size_t len; | |
69 | size_t buflen; | |
70 | int status; | |
71 | char part; | |
72 | char bodytype; | |
73 | bool error; | |
74 | }; | |
75 | ||
76 | struct http_connection_t | |
77 | { | |
78 | char *domain; | |
79 | char *location; | |
80 | char *urlcopy; | |
81 | char *scan; | |
82 | char *methodcopy; | |
83 | char *contenttypecopy; | |
84 | char *postdatacopy; | |
85 | char *useragentcopy; | |
86 | char *headerscopy; | |
87 | struct http_socket_state_t sock_state; /* ptr alignment */ | |
88 | int port; | |
89 | }; | |
90 | ||
91 | /** | |
92 | * net_http_urlencode: | |
93 | * | |
94 | * URL Encode a string | |
95 | * caller is responsible for deleting the destination buffer | |
96 | **/ | |
97 | void net_http_urlencode(char **dest, const char *source) | |
98 | { | |
99 | static const char urlencode_lut[256] = | |
100 | { | |
101 | 0, /* 0 */ | |
102 | 0, /* 1 */ | |
103 | 0, /* 2 */ | |
104 | 0, /* 3 */ | |
105 | 0, /* 4 */ | |
106 | 0, /* 5 */ | |
107 | 0, /* 6 */ | |
108 | 0, /* 7 */ | |
109 | 0, /* 8 */ | |
110 | 0, /* 9 */ | |
111 | 0, /* 10 */ | |
112 | 0, /* 11 */ | |
113 | 0, /* 12 */ | |
114 | 0, /* 13 */ | |
115 | 0, /* 14 */ | |
116 | 0, /* 15 */ | |
117 | 0, /* 16 */ | |
118 | 0, /* 17 */ | |
119 | 0, /* 18 */ | |
120 | 0, /* 19 */ | |
121 | 0, /* 20 */ | |
122 | 0, /* 21 */ | |
123 | 0, /* 22 */ | |
124 | 0, /* 23 */ | |
125 | 0, /* 24 */ | |
126 | 0, /* 25 */ | |
127 | 0, /* 26 */ | |
128 | 0, /* 27 */ | |
129 | 0, /* 28 */ | |
130 | 0, /* 29 */ | |
131 | 0, /* 30 */ | |
132 | 0, /* 31 */ | |
133 | 0, /* 32 */ | |
134 | 0, /* 33 */ | |
135 | 0, /* 34 */ | |
136 | 0, /* 35 */ | |
137 | 0, /* 36 */ | |
138 | 0, /* 37 */ | |
139 | 0, /* 38 */ | |
140 | 0, /* 39 */ | |
141 | 0, /* 40 */ | |
142 | 0, /* 41 */ | |
143 | '*', /* 42 */ | |
144 | 0, /* 43 */ | |
145 | 0, /* 44 */ | |
146 | '-', /* 45 */ | |
147 | '.', /* 46 */ | |
148 | '/', /* 47 */ | |
149 | '0', /* 48 */ | |
150 | '1', /* 49 */ | |
151 | '2', /* 50 */ | |
152 | '3', /* 51 */ | |
153 | '4', /* 52 */ | |
154 | '5', /* 53 */ | |
155 | '6', /* 54 */ | |
156 | '7', /* 55 */ | |
157 | '8', /* 56 */ | |
158 | '9', /* 57 */ | |
159 | 0, /* 58 */ | |
160 | 0, /* 59 */ | |
161 | 0, /* 60 */ | |
162 | 0, /* 61 */ | |
163 | 0, /* 62 */ | |
164 | 0, /* 63 */ | |
165 | 0, /* 64 */ | |
166 | 'A', /* 65 */ | |
167 | 'B', /* 66 */ | |
168 | 'C', /* 67 */ | |
169 | 'D', /* 68 */ | |
170 | 'E', /* 69 */ | |
171 | 'F', /* 70 */ | |
172 | 'G', /* 71 */ | |
173 | 'H', /* 72 */ | |
174 | 'I', /* 73 */ | |
175 | 'J', /* 74 */ | |
176 | 'K', /* 75 */ | |
177 | 'L', /* 76 */ | |
178 | 'M', /* 77 */ | |
179 | 'N', /* 78 */ | |
180 | 'O', /* 79 */ | |
181 | 'P', /* 80 */ | |
182 | 'Q', /* 81 */ | |
183 | 'R', /* 82 */ | |
184 | 'S', /* 83 */ | |
185 | 'T', /* 84 */ | |
186 | 'U', /* 85 */ | |
187 | 'V', /* 86 */ | |
188 | 'W', /* 87 */ | |
189 | 'X', /* 88 */ | |
190 | 'Y', /* 89 */ | |
191 | 'Z', /* 90 */ | |
192 | 0, /* 91 */ | |
193 | 0, /* 92 */ | |
194 | 0, /* 93 */ | |
195 | 0, /* 94 */ | |
196 | '_', /* 95 */ | |
197 | 0, /* 96 */ | |
198 | 'a', /* 97 */ | |
199 | 'b', /* 98 */ | |
200 | 'c', /* 99 */ | |
201 | 'd', /* 100 */ | |
202 | 'e', /* 101 */ | |
203 | 'f', /* 102 */ | |
204 | 'g', /* 103 */ | |
205 | 'h', /* 104 */ | |
206 | 'i', /* 105 */ | |
207 | 'j', /* 106 */ | |
208 | 'k', /* 107 */ | |
209 | 'l', /* 108 */ | |
210 | 'm', /* 109 */ | |
211 | 'n', /* 110 */ | |
212 | 'o', /* 111 */ | |
213 | 'p', /* 112 */ | |
214 | 'q', /* 113 */ | |
215 | 'r', /* 114 */ | |
216 | 's', /* 115 */ | |
217 | 't', /* 116 */ | |
218 | 'u', /* 117 */ | |
219 | 'v', /* 118 */ | |
220 | 'w', /* 119 */ | |
221 | 'x', /* 120 */ | |
222 | 'y', /* 121 */ | |
223 | 'z', /* 122 */ | |
224 | 0, /* 123 */ | |
225 | 0, /* 124 */ | |
226 | 0, /* 125 */ | |
227 | 0, /* 126 */ | |
228 | 0, /* 127 */ | |
229 | 0, /* 128 */ | |
230 | 0, /* 129 */ | |
231 | 0, /* 130 */ | |
232 | 0, /* 131 */ | |
233 | 0, /* 132 */ | |
234 | 0, /* 133 */ | |
235 | 0, /* 134 */ | |
236 | 0, /* 135 */ | |
237 | 0, /* 136 */ | |
238 | 0, /* 137 */ | |
239 | 0, /* 138 */ | |
240 | 0, /* 139 */ | |
241 | 0, /* 140 */ | |
242 | 0, /* 141 */ | |
243 | 0, /* 142 */ | |
244 | 0, /* 143 */ | |
245 | 0, /* 144 */ | |
246 | 0, /* 145 */ | |
247 | 0, /* 146 */ | |
248 | 0, /* 147 */ | |
249 | 0, /* 148 */ | |
250 | 0, /* 149 */ | |
251 | 0, /* 150 */ | |
252 | 0, /* 151 */ | |
253 | 0, /* 152 */ | |
254 | 0, /* 153 */ | |
255 | 0, /* 154 */ | |
256 | 0, /* 155 */ | |
257 | 0, /* 156 */ | |
258 | 0, /* 157 */ | |
259 | 0, /* 158 */ | |
260 | 0, /* 159 */ | |
261 | 0, /* 160 */ | |
262 | 0, /* 161 */ | |
263 | 0, /* 162 */ | |
264 | 0, /* 163 */ | |
265 | 0, /* 164 */ | |
266 | 0, /* 165 */ | |
267 | 0, /* 166 */ | |
268 | 0, /* 167 */ | |
269 | 0, /* 168 */ | |
270 | 0, /* 169 */ | |
271 | 0, /* 170 */ | |
272 | 0, /* 171 */ | |
273 | 0, /* 172 */ | |
274 | 0, /* 173 */ | |
275 | 0, /* 174 */ | |
276 | 0, /* 175 */ | |
277 | 0, /* 176 */ | |
278 | 0, /* 177 */ | |
279 | 0, /* 178 */ | |
280 | 0, /* 179 */ | |
281 | 0, /* 180 */ | |
282 | 0, /* 181 */ | |
283 | 0, /* 182 */ | |
284 | 0, /* 183 */ | |
285 | 0, /* 184 */ | |
286 | 0, /* 185 */ | |
287 | 0, /* 186 */ | |
288 | 0, /* 187 */ | |
289 | 0, /* 188 */ | |
290 | 0, /* 189 */ | |
291 | 0, /* 190 */ | |
292 | 0, /* 191 */ | |
293 | 0, /* 192 */ | |
294 | 0, /* 193 */ | |
295 | 0, /* 194 */ | |
296 | 0, /* 195 */ | |
297 | 0, /* 196 */ | |
298 | 0, /* 197 */ | |
299 | 0, /* 198 */ | |
300 | 0, /* 199 */ | |
301 | 0, /* 200 */ | |
302 | 0, /* 201 */ | |
303 | 0, /* 202 */ | |
304 | 0, /* 203 */ | |
305 | 0, /* 204 */ | |
306 | 0, /* 205 */ | |
307 | 0, /* 206 */ | |
308 | 0, /* 207 */ | |
309 | 0, /* 208 */ | |
310 | 0, /* 209 */ | |
311 | 0, /* 210 */ | |
312 | 0, /* 211 */ | |
313 | 0, /* 212 */ | |
314 | 0, /* 213 */ | |
315 | 0, /* 214 */ | |
316 | 0, /* 215 */ | |
317 | 0, /* 216 */ | |
318 | 0, /* 217 */ | |
319 | 0, /* 218 */ | |
320 | 0, /* 219 */ | |
321 | 0, /* 220 */ | |
322 | 0, /* 221 */ | |
323 | 0, /* 222 */ | |
324 | 0, /* 223 */ | |
325 | 0, /* 224 */ | |
326 | 0, /* 225 */ | |
327 | 0, /* 226 */ | |
328 | 0, /* 227 */ | |
329 | 0, /* 228 */ | |
330 | 0, /* 229 */ | |
331 | 0, /* 230 */ | |
332 | 0, /* 231 */ | |
333 | 0, /* 232 */ | |
334 | 0, /* 233 */ | |
335 | 0, /* 234 */ | |
336 | 0, /* 235 */ | |
337 | 0, /* 236 */ | |
338 | 0, /* 237 */ | |
339 | 0, /* 238 */ | |
340 | 0, /* 239 */ | |
341 | 0, /* 240 */ | |
342 | 0, /* 241 */ | |
343 | 0, /* 242 */ | |
344 | 0, /* 243 */ | |
345 | 0, /* 244 */ | |
346 | 0, /* 245 */ | |
347 | 0, /* 246 */ | |
348 | 0, /* 247 */ | |
349 | 0, /* 248 */ | |
350 | 0, /* 249 */ | |
351 | 0, /* 250 */ | |
352 | 0, /* 251 */ | |
353 | 0, /* 252 */ | |
354 | 0, /* 253 */ | |
355 | 0, /* 254 */ | |
356 | 0 /* 255 */ | |
357 | }; | |
358 | ||
359 | /* Assume every character will be encoded, so we need 3 times the space. */ | |
360 | size_t len = strlen(source) * 3 + 1; | |
361 | size_t count = len; | |
362 | char *enc = (char*)calloc(1, len); | |
363 | *dest = enc; | |
364 | ||
365 | for (; *source; source++) | |
366 | { | |
367 | int written = 0; | |
368 | ||
369 | /* any non-ASCII character will just be encoded without question */ | |
370 | if ((unsigned)*source < sizeof(urlencode_lut) && urlencode_lut[(unsigned)*source]) | |
371 | written = snprintf(enc, count, "%c", urlencode_lut[(unsigned)*source]); | |
372 | else | |
373 | written = snprintf(enc, count, "%%%02X", *source & 0xFF); | |
374 | ||
375 | if (written > 0) | |
376 | count -= written; | |
377 | ||
378 | while (*++enc); | |
379 | } | |
380 | ||
381 | (*dest)[len - 1] = '\0'; | |
382 | } | |
383 | ||
384 | /** | |
385 | * net_http_urlencode_full: | |
386 | * | |
387 | * Re-encode a full URL | |
388 | **/ | |
389 | void net_http_urlencode_full(char *dest, | |
390 | const char *source, size_t size) | |
391 | { | |
392 | size_t tmp_len; | |
393 | size_t url_domain_len; | |
394 | char url_domain[256]; | |
395 | char url_path[PATH_MAX_LENGTH]; | |
396 | size_t buf_pos = 0; | |
397 | char *tmp = NULL; | |
398 | int count = 0; | |
399 | ||
400 | strlcpy(url_path, source, sizeof(url_path)); | |
401 | tmp = url_path; | |
402 | ||
403 | while (count < 3 && tmp[0] != '\0') | |
404 | { | |
405 | tmp = strchr(tmp, '/'); | |
406 | count++; | |
407 | tmp++; | |
408 | } | |
409 | ||
410 | tmp_len = strlen(tmp); | |
411 | url_domain_len = ((strlcpy(url_domain, source, tmp - url_path)) - tmp_len) - 1; | |
412 | strlcpy(url_path, | |
413 | source + url_domain_len + 1, | |
414 | tmp_len + 1 | |
415 | ); | |
416 | ||
417 | tmp = NULL; | |
418 | net_http_urlencode(&tmp, url_path); | |
419 | buf_pos = strlcpy(dest, url_domain, size); | |
420 | dest[buf_pos] = '/'; | |
421 | dest[buf_pos+1] = '\0'; | |
422 | strlcat(dest, tmp, size); | |
423 | free (tmp); | |
424 | } | |
425 | ||
426 | static int net_http_new_socket(struct http_connection_t *conn) | |
427 | { | |
428 | struct addrinfo *addr = NULL, *next_addr = NULL; | |
429 | int fd = socket_init( | |
430 | (void**)&addr, conn->port, conn->domain, SOCKET_TYPE_STREAM, 0); | |
431 | #ifdef HAVE_SSL | |
432 | if (conn->sock_state.ssl) | |
433 | { | |
434 | if (fd < 0) | |
435 | goto done; | |
436 | ||
437 | if (!(conn->sock_state.ssl_ctx = ssl_socket_init(fd, conn->domain))) | |
438 | { | |
439 | socket_close(fd); | |
440 | fd = -1; | |
441 | goto done; | |
442 | } | |
443 | ||
444 | /* TODO: Properly figure out what's going wrong when the newer | |
445 | timeout/poll code interacts with mbed and winsock | |
446 | https://github.com/libretro/RetroArch/issues/14742 */ | |
447 | ||
448 | /* Temp fix, don't use new timeout/poll code for cheevos http requests */ | |
449 | bool timeout = true; | |
450 | #ifdef __WIN32 | |
451 | if (!strcmp(conn->domain, "retroachievements.org")) | |
452 | timeout = false; | |
453 | #endif | |
454 | ||
455 | if (ssl_socket_connect(conn->sock_state.ssl_ctx, addr, timeout, true) | |
456 | < 0) | |
457 | { | |
458 | fd = -1; | |
459 | goto done; | |
460 | } | |
461 | } | |
462 | else | |
463 | #endif | |
464 | for (next_addr = addr; fd >= 0; fd = socket_next((void**)&next_addr)) | |
465 | { | |
466 | if (socket_connect_with_timeout(fd, next_addr, 5000)) | |
467 | break; | |
468 | ||
469 | socket_close(fd); | |
470 | } | |
471 | ||
472 | #ifdef HAVE_SSL | |
473 | done: | |
474 | #endif | |
475 | if (addr) | |
476 | freeaddrinfo_retro(addr); | |
477 | ||
478 | conn->sock_state.fd = fd; | |
479 | ||
480 | return fd; | |
481 | } | |
482 | ||
483 | static void net_http_send_str( | |
484 | struct http_socket_state_t *sock_state, bool *error, | |
485 | const char *text, size_t text_size) | |
486 | { | |
487 | if (*error) | |
488 | return; | |
489 | #ifdef HAVE_SSL | |
490 | if (sock_state->ssl) | |
491 | { | |
492 | if (!ssl_socket_send_all_blocking( | |
493 | sock_state->ssl_ctx, text, text_size, true)) | |
494 | *error = true; | |
495 | } | |
496 | else | |
497 | #endif | |
498 | { | |
499 | if (!socket_send_all_blocking( | |
500 | sock_state->fd, text, text_size, true)) | |
501 | *error = true; | |
502 | } | |
503 | } | |
504 | ||
505 | struct http_connection_t *net_http_connection_new(const char *url, | |
506 | const char *method, const char *data) | |
507 | { | |
508 | struct http_connection_t *conn = NULL; | |
509 | ||
510 | if (!url) | |
511 | return NULL; | |
512 | if (!(conn = (struct http_connection_t*)malloc( | |
513 | sizeof(*conn)))) | |
514 | return NULL; | |
515 | ||
516 | conn->domain = NULL; | |
517 | conn->location = NULL; | |
518 | conn->urlcopy = NULL; | |
519 | conn->scan = NULL; | |
520 | conn->methodcopy = NULL; | |
521 | conn->contenttypecopy = NULL; | |
522 | conn->postdatacopy = NULL; | |
523 | conn->useragentcopy = NULL; | |
524 | conn->headerscopy = NULL; | |
525 | conn->port = 0; | |
526 | conn->sock_state.fd = 0; | |
527 | conn->sock_state.ssl = false; | |
528 | conn->sock_state.ssl_ctx= NULL; | |
529 | ||
530 | if (method) | |
531 | conn->methodcopy = strdup(method); | |
532 | ||
533 | if (data) | |
534 | conn->postdatacopy = strdup(data); | |
535 | ||
536 | if (!(conn->urlcopy = strdup(url))) | |
537 | goto error; | |
538 | ||
539 | if (!strncmp(url, "http://", STRLEN_CONST("http://"))) | |
540 | conn->scan = conn->urlcopy + STRLEN_CONST("http://"); | |
541 | else if (!strncmp(url, "https://", STRLEN_CONST("https://"))) | |
542 | { | |
543 | conn->scan = conn->urlcopy + STRLEN_CONST("https://"); | |
544 | conn->sock_state.ssl = true; | |
545 | } | |
546 | else | |
547 | goto error; | |
548 | ||
549 | if (string_is_empty(conn->scan)) | |
550 | goto error; | |
551 | ||
552 | conn->domain = conn->scan; | |
553 | ||
554 | return conn; | |
555 | ||
556 | error: | |
557 | if (conn->urlcopy) | |
558 | free(conn->urlcopy); | |
559 | if (conn->methodcopy) | |
560 | free(conn->methodcopy); | |
561 | if (conn->postdatacopy) | |
562 | free(conn->postdatacopy); | |
563 | conn->urlcopy = NULL; | |
564 | conn->methodcopy = NULL; | |
565 | conn->postdatacopy = NULL; | |
566 | free(conn); | |
567 | return NULL; | |
568 | } | |
569 | ||
570 | /** | |
571 | * net_http_connection_iterate: | |
572 | * | |
573 | * Leaf function. | |
574 | **/ | |
575 | bool net_http_connection_iterate(struct http_connection_t *conn) | |
576 | { | |
577 | if (!conn) | |
578 | return false; | |
579 | ||
580 | while (*conn->scan != '/' && *conn->scan != ':' && *conn->scan != '\0') | |
581 | conn->scan++; | |
582 | ||
583 | return true; | |
584 | } | |
585 | ||
586 | bool net_http_connection_done(struct http_connection_t *conn) | |
587 | { | |
588 | int has_port = 0; | |
589 | ||
590 | if (!conn || !conn->domain || !*conn->domain) | |
591 | return false; | |
592 | ||
593 | if (*conn->scan == ':') | |
594 | { | |
595 | /* domain followed by port, split off the port */ | |
596 | *conn->scan++ = '\0'; | |
597 | ||
598 | if (!isdigit((int)(*conn->scan))) | |
599 | return false; | |
600 | ||
601 | conn->port = (int)strtoul(conn->scan, &conn->scan, 10); | |
602 | has_port = 1; | |
603 | } | |
604 | else if (conn->port == 0) | |
605 | { | |
606 | /* port not specified, default to standard HTTP or HTTPS port */ | |
607 | if (conn->sock_state.ssl) | |
608 | conn->port = 443; | |
609 | else | |
610 | conn->port = 80; | |
611 | } | |
612 | ||
613 | if (*conn->scan == '/') | |
614 | { | |
615 | /* domain followed by location - split off the location */ | |
616 | /* site.com/path.html or site.com:80/path.html */ | |
617 | *conn->scan = '\0'; | |
618 | conn->location = conn->scan + 1; | |
619 | return true; | |
620 | } | |
621 | else if (!*conn->scan) | |
622 | { | |
623 | /* domain with no location - point location at empty string */ | |
624 | /* site.com or site.com:80 */ | |
625 | conn->location = conn->scan; | |
626 | return true; | |
627 | } | |
628 | else if (*conn->scan == '?') | |
629 | { | |
630 | /* domain with no location, but still has query parms - point location at the query parms */ | |
631 | /* site.com?param=3 or site.com:80?param=3 */ | |
632 | if (!has_port) | |
633 | { | |
634 | /* if there wasn't a port, we have to expand the urlcopy so we can separate the two parts */ | |
635 | size_t domain_len = strlen(conn->domain); | |
636 | size_t location_len = strlen(conn->scan); | |
637 | char* urlcopy = (char*)malloc(domain_len + location_len + 2); | |
638 | memcpy(urlcopy, conn->domain, domain_len); | |
639 | urlcopy[domain_len] = '\0'; | |
640 | memcpy(urlcopy + domain_len + 1, conn->scan, location_len + 1); | |
641 | ||
642 | free(conn->urlcopy); | |
643 | conn->domain = conn->urlcopy = urlcopy; | |
644 | conn->location = conn->scan = urlcopy + domain_len + 1; | |
645 | } | |
646 | else /* There was a port, so overwriting the : will terminate the domain and we can just point at the ? */ | |
647 | conn->location = conn->scan; | |
648 | ||
649 | return true; | |
650 | } | |
651 | ||
652 | /* invalid character after domain/port */ | |
653 | return false; | |
654 | } | |
655 | ||
656 | void net_http_connection_free(struct http_connection_t *conn) | |
657 | { | |
658 | if (!conn) | |
659 | return; | |
660 | ||
661 | if (conn->urlcopy) | |
662 | free(conn->urlcopy); | |
663 | ||
664 | if (conn->methodcopy) | |
665 | free(conn->methodcopy); | |
666 | ||
667 | if (conn->contenttypecopy) | |
668 | free(conn->contenttypecopy); | |
669 | ||
670 | if (conn->postdatacopy) | |
671 | free(conn->postdatacopy); | |
672 | ||
673 | if (conn->useragentcopy) | |
674 | free(conn->useragentcopy); | |
675 | ||
676 | if (conn->headerscopy) | |
677 | free(conn->headerscopy); | |
678 | ||
679 | conn->urlcopy = NULL; | |
680 | conn->methodcopy = NULL; | |
681 | conn->contenttypecopy = NULL; | |
682 | conn->postdatacopy = NULL; | |
683 | conn->useragentcopy = NULL; | |
684 | conn->headerscopy = NULL; | |
685 | ||
686 | free(conn); | |
687 | } | |
688 | ||
689 | void net_http_connection_set_user_agent( | |
690 | struct http_connection_t *conn, const char *user_agent) | |
691 | { | |
692 | if (conn->useragentcopy) | |
693 | free(conn->useragentcopy); | |
694 | ||
695 | conn->useragentcopy = user_agent ? strdup(user_agent) : NULL; | |
696 | } | |
697 | ||
698 | void net_http_connection_set_headers( | |
699 | struct http_connection_t *conn, const char *headers) | |
700 | { | |
701 | if (conn->headerscopy) | |
702 | free(conn->headerscopy); | |
703 | ||
704 | conn->headerscopy = headers ? strdup(headers) : NULL; | |
705 | } | |
706 | ||
707 | const char *net_http_connection_url(struct http_connection_t *conn) | |
708 | { | |
709 | return conn->urlcopy; | |
710 | } | |
711 | ||
712 | const char* net_http_connection_method(struct http_connection_t* conn) | |
713 | { | |
714 | return conn->methodcopy; | |
715 | } | |
716 | ||
717 | struct http_t *net_http_new(struct http_connection_t *conn) | |
718 | { | |
719 | bool error = false; | |
720 | int fd = -1; | |
721 | struct http_t *state = NULL; | |
722 | ||
723 | if (!conn) | |
724 | goto error; | |
725 | ||
726 | if ((fd = net_http_new_socket(conn)) < 0) | |
727 | goto error; | |
728 | ||
729 | error = false; | |
730 | ||
731 | /* This is a bit lazy, but it works. */ | |
732 | if (conn->methodcopy) | |
733 | { | |
734 | net_http_send_str(&conn->sock_state, &error, conn->methodcopy, | |
735 | strlen(conn->methodcopy)); | |
736 | net_http_send_str(&conn->sock_state, &error, " /", | |
737 | STRLEN_CONST(" /")); | |
738 | } | |
739 | else | |
740 | { | |
741 | net_http_send_str(&conn->sock_state, &error, "GET /", | |
742 | STRLEN_CONST("GET /")); | |
743 | } | |
744 | ||
745 | net_http_send_str(&conn->sock_state, &error, conn->location, | |
746 | strlen(conn->location)); | |
747 | net_http_send_str(&conn->sock_state, &error, " HTTP/1.1\r\n", | |
748 | STRLEN_CONST(" HTTP/1.1\r\n")); | |
749 | ||
750 | net_http_send_str(&conn->sock_state, &error, "Host: ", | |
751 | STRLEN_CONST("Host: ")); | |
752 | net_http_send_str(&conn->sock_state, &error, conn->domain, | |
753 | strlen(conn->domain)); | |
754 | ||
755 | if (conn->port) | |
756 | { | |
757 | char portstr[16]; | |
758 | ||
759 | portstr[0] = '\0'; | |
760 | ||
761 | snprintf(portstr, sizeof(portstr), ":%i", conn->port); | |
762 | net_http_send_str(&conn->sock_state, &error, portstr, | |
763 | strlen(portstr)); | |
764 | } | |
765 | ||
766 | net_http_send_str(&conn->sock_state, &error, "\r\n", | |
767 | STRLEN_CONST("\r\n")); | |
768 | ||
769 | /* Pre-formatted headers */ | |
770 | if (conn->headerscopy) | |
771 | net_http_send_str(&conn->sock_state, &error, conn->headerscopy, | |
772 | strlen(conn->headerscopy)); | |
773 | /* This is not being set anywhere yet */ | |
774 | else if (conn->contenttypecopy) | |
775 | { | |
776 | net_http_send_str(&conn->sock_state, &error, "Content-Type: ", | |
777 | STRLEN_CONST("Content-Type: ")); | |
778 | net_http_send_str(&conn->sock_state, &error, | |
779 | conn->contenttypecopy, strlen(conn->contenttypecopy)); | |
780 | net_http_send_str(&conn->sock_state, &error, "\r\n", | |
781 | STRLEN_CONST("\r\n")); | |
782 | } | |
783 | ||
784 | if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST"))) | |
785 | { | |
786 | size_t post_len, len; | |
787 | char *len_str = NULL; | |
788 | ||
789 | if (!conn->postdatacopy) | |
790 | goto error; | |
791 | ||
792 | if (!conn->headerscopy) | |
793 | { | |
794 | if (!conn->contenttypecopy) | |
795 | net_http_send_str(&conn->sock_state, &error, | |
796 | "Content-Type: application/x-www-form-urlencoded\r\n", | |
797 | STRLEN_CONST( | |
798 | "Content-Type: application/x-www-form-urlencoded\r\n" | |
799 | )); | |
800 | } | |
801 | ||
802 | net_http_send_str(&conn->sock_state, &error, "Content-Length: ", | |
803 | STRLEN_CONST("Content-Length: ")); | |
804 | ||
805 | post_len = strlen(conn->postdatacopy); | |
806 | #ifdef _WIN32 | |
807 | len = snprintf(NULL, 0, "%" PRIuPTR, post_len); | |
808 | len_str = (char*)malloc(len + 1); | |
809 | snprintf(len_str, len + 1, "%" PRIuPTR, post_len); | |
810 | #else | |
811 | len = snprintf(NULL, 0, "%llu", (long long unsigned)post_len); | |
812 | len_str = (char*)malloc(len + 1); | |
813 | snprintf(len_str, len + 1, "%llu", (long long unsigned)post_len); | |
814 | #endif | |
815 | ||
816 | len_str[len] = '\0'; | |
817 | ||
818 | net_http_send_str(&conn->sock_state, &error, len_str, | |
819 | strlen(len_str)); | |
820 | net_http_send_str(&conn->sock_state, &error, "\r\n", | |
821 | STRLEN_CONST("\r\n")); | |
822 | ||
823 | free(len_str); | |
824 | } | |
825 | ||
826 | net_http_send_str(&conn->sock_state, &error, "User-Agent: ", | |
827 | STRLEN_CONST("User-Agent: ")); | |
828 | if (conn->useragentcopy) | |
829 | net_http_send_str(&conn->sock_state, &error, | |
830 | conn->useragentcopy, strlen(conn->useragentcopy)); | |
831 | else | |
832 | net_http_send_str(&conn->sock_state, &error, "libretro", | |
833 | STRLEN_CONST("libretro")); | |
834 | net_http_send_str(&conn->sock_state, &error, "\r\n", | |
835 | STRLEN_CONST("\r\n")); | |
836 | ||
837 | net_http_send_str(&conn->sock_state, &error, | |
838 | "Connection: close\r\n", STRLEN_CONST("Connection: close\r\n")); | |
839 | net_http_send_str(&conn->sock_state, &error, "\r\n", | |
840 | STRLEN_CONST("\r\n")); | |
841 | ||
842 | if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST"))) | |
843 | net_http_send_str(&conn->sock_state, &error, conn->postdatacopy, | |
844 | strlen(conn->postdatacopy)); | |
845 | ||
846 | if (error) | |
847 | goto error; | |
848 | ||
849 | state = (struct http_t*)malloc(sizeof(struct http_t)); | |
850 | state->sock_state = conn->sock_state; | |
851 | state->status = -1; | |
852 | state->data = NULL; | |
853 | state->part = P_HEADER_TOP; | |
854 | state->bodytype = T_FULL; | |
855 | state->error = false; | |
856 | state->pos = 0; | |
857 | state->len = 0; | |
858 | state->buflen = 512; | |
859 | ||
860 | if (!(state->data = (char*)malloc(state->buflen))) | |
861 | goto error; | |
862 | ||
863 | return state; | |
864 | ||
865 | error: | |
866 | if (conn) | |
867 | { | |
868 | if (conn->methodcopy) | |
869 | free(conn->methodcopy); | |
870 | if (conn->contenttypecopy) | |
871 | free(conn->contenttypecopy); | |
872 | conn->methodcopy = NULL; | |
873 | conn->contenttypecopy = NULL; | |
874 | conn->postdatacopy = NULL; | |
875 | } | |
876 | #ifdef HAVE_SSL | |
877 | if (conn && conn->sock_state.ssl_ctx) | |
878 | { | |
879 | ssl_socket_close(conn->sock_state.ssl_ctx); | |
880 | ssl_socket_free(conn->sock_state.ssl_ctx); | |
881 | conn->sock_state.ssl_ctx = NULL; | |
882 | } | |
883 | #else | |
884 | if (fd >= 0) | |
885 | socket_close(fd); | |
886 | #endif | |
887 | if (state) | |
888 | free(state); | |
889 | return NULL; | |
890 | } | |
891 | ||
892 | /** | |
893 | * net_http_fd: | |
894 | * | |
895 | * Leaf function. | |
896 | * | |
897 | * You can use this to call net_http_update | |
898 | * only when something will happen; select() it for reading. | |
899 | **/ | |
900 | int net_http_fd(struct http_t *state) | |
901 | { | |
902 | if (!state) | |
903 | return -1; | |
904 | return state->sock_state.fd; | |
905 | } | |
906 | ||
907 | /** | |
908 | * net_http_update: | |
909 | * | |
910 | * @return true if it's done, or if something broke. | |
911 | * @total will be 0 if it's not known. | |
912 | **/ | |
913 | bool net_http_update(struct http_t *state, size_t* progress, size_t* total) | |
914 | { | |
915 | ssize_t newlen = 0; | |
916 | ||
917 | if (!state) | |
918 | return true; | |
919 | if (state->error) | |
920 | { | |
921 | state->part = P_ERROR; | |
922 | state->status = -1; | |
923 | return true; | |
924 | } | |
925 | ||
926 | if (state->part < P_BODY) | |
927 | { | |
928 | if (state->error) | |
929 | { | |
930 | state->part = P_ERROR; | |
931 | state->status = -1; | |
932 | return true; | |
933 | } | |
934 | ||
935 | #ifdef HAVE_SSL | |
936 | if (state->sock_state.ssl && state->sock_state.ssl_ctx) | |
937 | newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error, | |
938 | (uint8_t*)state->data + state->pos, | |
939 | state->buflen - state->pos); | |
940 | else | |
941 | #endif | |
942 | newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error, | |
943 | (uint8_t*)state->data + state->pos, | |
944 | state->buflen - state->pos); | |
945 | ||
946 | if (newlen < 0) | |
947 | { | |
948 | state->error = true; | |
949 | state->part = P_ERROR; | |
950 | state->status = -1; | |
951 | return true; | |
952 | } | |
953 | ||
954 | if (state->pos + newlen >= state->buflen - 64) | |
955 | { | |
956 | state->buflen *= 2; | |
957 | state->data = (char*)realloc(state->data, state->buflen); | |
958 | } | |
959 | state->pos += newlen; | |
960 | ||
961 | while (state->part < P_BODY) | |
962 | { | |
963 | char *dataend = state->data + state->pos; | |
964 | char *lineend = (char*)memchr(state->data, '\n', state->pos); | |
965 | ||
966 | if (!lineend) | |
967 | break; | |
968 | ||
969 | *lineend='\0'; | |
970 | ||
971 | if (lineend != state->data && lineend[-1]=='\r') | |
972 | lineend[-1]='\0'; | |
973 | ||
974 | if (state->part == P_HEADER_TOP) | |
975 | { | |
976 | if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0) | |
977 | { | |
978 | state->error = true; | |
979 | state->part = P_ERROR; | |
980 | state->status = -1; | |
981 | return true; | |
982 | } | |
983 | state->status = (int)strtoul(state->data | |
984 | + STRLEN_CONST("HTTP/1.1 "), NULL, 10); | |
985 | state->part = P_HEADER; | |
986 | } | |
987 | else | |
988 | { | |
989 | if (string_starts_with_case_insensitive(state->data, "Content-Length:")) | |
990 | { | |
991 | char* ptr = state->data + STRLEN_CONST("Content-Length:"); | |
992 | while (ISSPACE(*ptr)) | |
993 | ++ptr; | |
994 | ||
995 | state->bodytype = T_LEN; | |
996 | state->len = strtol(ptr, NULL, 10); | |
997 | } | |
998 | if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked")) | |
999 | state->bodytype = T_CHUNK; | |
1000 | ||
1001 | /* TODO: save headers somewhere */ | |
1002 | if (state->data[0]=='\0') | |
1003 | { | |
1004 | state->part = P_BODY; | |
1005 | if (state->bodytype == T_CHUNK) | |
1006 | state->part = P_BODY_CHUNKLEN; | |
1007 | } | |
1008 | } | |
1009 | ||
1010 | memmove(state->data, lineend + 1, dataend-(lineend+1)); | |
1011 | state->pos = (dataend-(lineend + 1)); | |
1012 | } | |
1013 | ||
1014 | if (state->part >= P_BODY) | |
1015 | { | |
1016 | newlen = state->pos; | |
1017 | state->pos = 0; | |
1018 | } | |
1019 | } | |
1020 | ||
1021 | if (state->part >= P_BODY && state->part < P_DONE) | |
1022 | { | |
1023 | if (!newlen) | |
1024 | { | |
1025 | if (state->error) | |
1026 | newlen = -1; | |
1027 | else | |
1028 | { | |
1029 | #ifdef HAVE_SSL | |
1030 | if (state->sock_state.ssl && state->sock_state.ssl_ctx) | |
1031 | newlen = ssl_socket_receive_all_nonblocking( | |
1032 | state->sock_state.ssl_ctx, | |
1033 | &state->error, | |
1034 | (uint8_t*)state->data + state->pos, | |
1035 | state->buflen - state->pos); | |
1036 | else | |
1037 | #endif | |
1038 | newlen = socket_receive_all_nonblocking( | |
1039 | state->sock_state.fd, | |
1040 | &state->error, | |
1041 | (uint8_t*)state->data + state->pos, | |
1042 | state->buflen - state->pos); | |
1043 | } | |
1044 | ||
1045 | if (newlen < 0) | |
1046 | { | |
1047 | if (state->bodytype != T_FULL) | |
1048 | { | |
1049 | state->error = true; | |
1050 | state->part = P_ERROR; | |
1051 | state->status = -1; | |
1052 | return true; | |
1053 | } | |
1054 | state->part = P_DONE; | |
1055 | state->data = (char*)realloc(state->data, state->len); | |
1056 | newlen = 0; | |
1057 | } | |
1058 | ||
1059 | if (state->pos + newlen >= state->buflen - 64) | |
1060 | { | |
1061 | state->buflen *= 2; | |
1062 | state->data = (char*)realloc(state->data, state->buflen); | |
1063 | } | |
1064 | } | |
1065 | ||
1066 | parse_again: | |
1067 | if (state->bodytype == T_CHUNK) | |
1068 | { | |
1069 | if (state->part == P_BODY_CHUNKLEN) | |
1070 | { | |
1071 | state->pos += newlen; | |
1072 | ||
1073 | if (state->pos - state->len >= 2) | |
1074 | { | |
1075 | /* | |
1076 | * len=start of chunk including \r\n | |
1077 | * pos=end of data | |
1078 | */ | |
1079 | ||
1080 | char *fullend = state->data + state->pos; | |
1081 | char *end = (char*)memchr(state->data + state->len + 2, '\n', | |
1082 | state->pos - state->len - 2); | |
1083 | ||
1084 | if (end) | |
1085 | { | |
1086 | size_t chunklen = strtoul(state->data+state->len, NULL, 16); | |
1087 | state->pos = state->len; | |
1088 | end++; | |
1089 | ||
1090 | memmove(state->data+state->len, end, fullend-end); | |
1091 | ||
1092 | state->len = chunklen; | |
1093 | newlen = (fullend - end); | |
1094 | ||
1095 | /* | |
1096 | len=num bytes | |
1097 | newlen=unparsed bytes after \n | |
1098 | pos=start of chunk including \r\n | |
1099 | */ | |
1100 | ||
1101 | state->part = P_BODY; | |
1102 | if (state->len == 0) | |
1103 | { | |
1104 | state->part = P_DONE; | |
1105 | state->len = state->pos; | |
1106 | state->data = (char*)realloc(state->data, state->len); | |
1107 | } | |
1108 | goto parse_again; | |
1109 | } | |
1110 | } | |
1111 | } | |
1112 | else if (state->part == P_BODY) | |
1113 | { | |
1114 | if ((size_t)newlen >= state->len) | |
1115 | { | |
1116 | state->pos += state->len; | |
1117 | newlen -= state->len; | |
1118 | state->len = state->pos; | |
1119 | state->part = P_BODY_CHUNKLEN; | |
1120 | goto parse_again; | |
1121 | } | |
1122 | state->pos += newlen; | |
1123 | state->len -= newlen; | |
1124 | } | |
1125 | } | |
1126 | else | |
1127 | { | |
1128 | state->pos += newlen; | |
1129 | ||
1130 | if (state->pos > state->len) | |
1131 | { | |
1132 | state->error = true; | |
1133 | state->part = P_ERROR; | |
1134 | state->status = -1; | |
1135 | return true; | |
1136 | } | |
1137 | else if (state->pos == state->len) | |
1138 | { | |
1139 | state->part = P_DONE; | |
1140 | state->data = (char*)realloc(state->data, state->len); | |
1141 | } | |
1142 | } | |
1143 | } | |
1144 | ||
1145 | if (progress) | |
1146 | *progress = state->pos; | |
1147 | ||
1148 | if (total) | |
1149 | { | |
1150 | if (state->bodytype == T_LEN) | |
1151 | *total = state->len; | |
1152 | else | |
1153 | *total = 0; | |
1154 | } | |
1155 | ||
1156 | return (state->part == P_DONE); | |
1157 | } | |
1158 | ||
1159 | /** | |
1160 | * net_http_status: | |
1161 | * | |
1162 | * Report HTTP status. 200, 404, or whatever. | |
1163 | * | |
1164 | * Leaf function. | |
1165 | * | |
1166 | * @return HTTP status code. | |
1167 | **/ | |
1168 | int net_http_status(struct http_t *state) | |
1169 | { | |
1170 | if (!state) | |
1171 | return -1; | |
1172 | return state->status; | |
1173 | } | |
1174 | ||
1175 | /** | |
1176 | * net_http_data: | |
1177 | * | |
1178 | * Leaf function. | |
1179 | * | |
1180 | * @return the downloaded data. The returned buffer is owned by the | |
1181 | * HTTP handler; it's freed by net_http_delete(). | |
1182 | * If the status is not 20x and accept_error is false, it returns NULL. | |
1183 | **/ | |
1184 | uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error) | |
1185 | { | |
1186 | if (!state) | |
1187 | return NULL; | |
1188 | ||
1189 | if (!accept_error && (state->error || state->status < 200 || state->status > 299)) | |
1190 | { | |
1191 | if (len) | |
1192 | *len = 0; | |
1193 | return NULL; | |
1194 | } | |
1195 | ||
1196 | if (len) | |
1197 | *len = state->len; | |
1198 | ||
1199 | return (uint8_t*)state->data; | |
1200 | } | |
1201 | ||
1202 | /** | |
1203 | * net_http_delete: | |
1204 | * | |
1205 | * Cleans up all memory. | |
1206 | **/ | |
1207 | void net_http_delete(struct http_t *state) | |
1208 | { | |
1209 | if (!state) | |
1210 | return; | |
1211 | ||
1212 | if (state->sock_state.fd >= 0) | |
1213 | { | |
1214 | #ifdef HAVE_SSL | |
1215 | if (state->sock_state.ssl && state->sock_state.ssl_ctx) | |
1216 | { | |
1217 | ssl_socket_close(state->sock_state.ssl_ctx); | |
1218 | ssl_socket_free(state->sock_state.ssl_ctx); | |
1219 | state->sock_state.ssl_ctx = NULL; | |
1220 | } | |
1221 | else | |
1222 | #endif | |
1223 | socket_close(state->sock_state.fd); | |
1224 | } | |
1225 | free(state); | |
1226 | } | |
1227 | ||
1228 | /** | |
1229 | * net_http_error: | |
1230 | * | |
1231 | * Leaf function | |
1232 | **/ | |
1233 | bool net_http_error(struct http_t *state) | |
1234 | { | |
1235 | return (state->error || state->status < 200 || state->status > 299); | |
1236 | } |