NetSurf
curl.c
Go to the documentation of this file.
1/*
2 * Copyright 2006-2019 Daniel Silverstone <dsilvers@digital-scurf.org>
3 * Copyright 2010-2018 Vincent Sanders <vince@netsurf-browser.org>
4 * Copyright 2007 James Bursa <bursa@users.sourceforge.net>
5 *
6 * This file is part of NetSurf.
7 *
8 * NetSurf is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * NetSurf is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * \file
23 * implementation of fetching of data from http and https schemes.
24 *
25 * This implementation uses libcurl's 'multi' interface.
26 *
27 * The CURL handles are cached in the curl_handle_ring.
28 */
29
30/* must come first to ensure winsock2.h vs windows.h ordering issues */
31#include "utils/inet.h"
32
33#include <assert.h>
34#include <errno.h>
35#include <inttypes.h>
36#include <stdbool.h>
37#include <string.h>
38#include <strings.h>
39#include <time.h>
40#include <sys/stat.h>
41
42#include <libwapcaplet/libwapcaplet.h>
43#include <nsutils/time.h>
44
45#include "utils/corestrings.h"
46#include "utils/hashmap.h"
47#include "utils/nsoption.h"
48#include "utils/log.h"
49#include "utils/messages.h"
50#include "utils/utils.h"
51#include "utils/ring.h"
52#include "utils/useragent.h"
53#include "utils/file.h"
54#include "utils/string.h"
55#include "netsurf/fetch.h"
56#include "netsurf/misc.h"
58
59#include "content/fetch.h"
60#include "content/fetchers.h"
62#include "content/urldb.h"
63
64/**
65 * maximum number of progress notifications per second
66 */
67#define UPDATES_PER_SECOND 2
68
69/**
70 * The ciphersuites the browser is prepared to use for TLS1.3
71 */
72#define CIPHER_SUITES \
73 "TLS_AES_256_GCM_SHA384:" \
74 "TLS_CHACHA20_POLY1305_SHA256:" \
75 "TLS_AES_128_GCM_SHA256"
76
77/**
78 * The ciphersuites the browser is prepared to use for TLS<1.3
79 */
80#define CIPHER_LIST \
81 /* disable everything */ \
82 "-ALL:" \
83 /* enable TLSv1.2 ECDHE AES GCM suites */ \
84 "EECDH+AESGCM+TLSv1.2:" \
85 /* enable ECDHE CHACHA20/POLY1305 suites */ \
86 "EECDH+CHACHA20:" \
87 /* Sort above by strength */ \
88 "@STRENGTH:" \
89 /* enable ECDHE (auth=RSA, mac=SHA1) AES CBC suites */ \
90 "EECDH+aRSA+AES+SHA1"
91
92/**
93 * The legacy cipher suites the browser is prepared to use for TLS<1.3
94 */
95#define CIPHER_LIST_LEGACY \
96 /* as above */ \
97 CIPHER_LIST":" \
98 /* enable (non-PFS) RSA AES GCM suites */ \
99 "RSA+AESGCM:" \
100 /* enable (non-PFS) RSA (mac=SHA1) AES CBC suites */ \
101 "RSA+AES+SHA1"
102
103/* Open SSL compatability for certificate handling */
104#ifdef WITH_OPENSSL
105
106#include <openssl/ssl.h>
107#include <openssl/x509v3.h>
108
109#else /* WITH_OPENSSL */
110
111typedef char X509;
112
113static void X509_free(X509 *cert)
114{
115 free(cert);
116}
117
118#endif /* WITH_OPENSSL */
119
120/* SSL certificate chain cache */
121
122/* We're only interested in the hostname and port */
123static uint32_t
125{
126 nsurl *url = key;
127 lwc_string *hostname = nsurl_get_component(url, NSURL_HOST);
128 lwc_string *port = nsurl_get_component(url, NSURL_PORT);
129 uint32_t hash;
130
131 if (port == NULL)
132 port = lwc_string_ref(corestring_lwc_443);
133
134 hash = lwc_string_hash_value(hostname) ^ lwc_string_hash_value(port);
135
136 lwc_string_unref(hostname);
137 lwc_string_unref(port);
138
139 return hash;
140}
141
142/* We only compare the hostname and port */
143static bool
144curl_fetch_ssl_key_eq(void *key1, void *key2)
145{
146 nsurl *url1 = key1;
147 nsurl *url2 = key2;
148 lwc_string *hostname1 = nsurl_get_component(url1, NSURL_HOST);
149 lwc_string *hostname2 = nsurl_get_component(url2, NSURL_HOST);
150 lwc_string *port1 = nsurl_get_component(url1, NSURL_PORT);
151 lwc_string *port2 = nsurl_get_component(url2, NSURL_PORT);
152 bool iseq = false;
153
154 if (port1 == NULL)
155 port1 = lwc_string_ref(corestring_lwc_443);
156 if (port2 == NULL)
157 port2 = lwc_string_ref(corestring_lwc_443);
158
159 if (lwc_string_isequal(hostname1, hostname2, &iseq) != lwc_error_ok ||
160 iseq == false)
161 goto out;
162
163 iseq = false;
164 if (lwc_string_isequal(port1, port2, &iseq) != lwc_error_ok)
165 goto out;
166
167out:
168 lwc_string_unref(hostname1);
169 lwc_string_unref(hostname2);
170 lwc_string_unref(port1);
171 lwc_string_unref(port2);
172
173 return iseq;
174}
175
176static void *
178{
179 struct cert_chain *out;
180
181 if (cert_chain_alloc(0, &out) != NSERROR_OK) {
182 return NULL;
183 }
184
185 return out;
186}
187
188static void
190{
191 struct cert_chain *chain = value;
192 if (cert_chain_free(chain) != NSERROR_OK) {
193 NSLOG(netsurf, WARNING, "Problem freeing SSL certificate chain");
194 }
195}
196
199 .key_destroy = (hashmap_key_destroy_t)nsurl_unref,
200 .key_eq = curl_fetch_ssl_key_eq,
201 .key_hash = curl_fetch_ssl_key_hash,
202 .value_alloc = curl_fetch_ssl_value_alloc,
203 .value_destroy = curl_fetch_ssl_value_destroy,
204};
205
207
208/** SSL certificate info */
209struct cert_info {
210 X509 *cert; /**< Pointer to certificate */
211 long err; /**< OpenSSL error code */
212};
213
214#if LIBCURL_VERSION_NUM >= 0x072000 /* 7.32.0 depricated CURLOPT_PROGRESSFUNCTION*/
215#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_XFERINFOFUNCTION
216#define NSCURLOPT_PROGRESS_DATA CURLOPT_XFERINFODATA
217#define NSCURL_PROGRESS_T curl_off_t
218#else
219#define NSCURLOPT_PROGRESS_FUNCTION CURLOPT_PROGRESSFUNCTION
220#define NSCURLOPT_PROGRESS_DATA CURLOPT_PROGRESSDATA
221#define NSCURL_PROGRESS_T double
222#endif
223
224#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */
225#define NSCURL_POSTDATA_T curl_mime
226#define NSCURL_POSTDATA_CURLOPT CURLOPT_MIMEPOST
227#define NSCURL_POSTDATA_FREE(x) curl_mime_free(x)
228#else
229#define NSCURL_POSTDATA_T struct curl_httppost
230#define NSCURL_POSTDATA_CURLOPT CURLOPT_HTTPPOST
231#define NSCURL_POSTDATA_FREE(x) curl_formfree(x)
232#endif
233
234/** Information for a single fetch. */
236 struct fetch *fetch_handle; /**< The fetch handle we're parented by. */
237 CURL * curl_handle; /**< cURL handle if being fetched, or 0. */
238 bool sent_ssl_chain; /**< Have we tried to send the SSL chain */
239 bool had_headers; /**< Headers have been processed. */
240 bool abort; /**< Abort requested. */
241 bool stopped; /**< Download stopped on purpose. */
242 bool only_2xx; /**< Only HTTP 2xx responses acceptable. */
243 bool downgrade_tls; /**< Downgrade to TLS 1.2 */
244 nsurl *url; /**< URL of this fetch. */
245 lwc_string *host; /**< The hostname of this fetch. */
246 struct curl_slist *headers; /**< List of request headers. */
247 char *location; /**< Response Location header, or 0. */
248 unsigned long content_length; /**< Response Content-Length, or 0. */
249 char *cookie_string; /**< Cookie string for this fetch */
250 char *realm; /**< HTTP Auth Realm */
251 struct fetch_postdata *postdata; /**< POST data */
252 NSCURL_POSTDATA_T *curl_postdata; /**< POST data in curl representation */
253
254 long http_code; /**< HTTP result code from cURL. */
255
256 uint64_t last_progress_update; /**< Time of last progress update */
257 int cert_depth; /**< deepest certificate in use */
258 struct cert_info cert_data[MAX_CERT_DEPTH]; /**< HTTPS certificate data */
259};
260
261/** curl handle cache entry */
263 CURL *handle; /**< The cached cURL handle */
264 lwc_string *host; /**< The host for which this handle is cached */
265
266 struct cache_handle *r_prev; /**< Previous cached handle in ring. */
267 struct cache_handle *r_next; /**< Next cached handle in ring. */
268};
269
270/** Global cURL multi handle. */
272
273/** Curl handle with default options set; not used for transfers. */
274static CURL *fetch_blank_curl;
275
276/** Ring of cached handles */
278
279/** Count of how many schemes the curl fetcher is handling */
281
282/** Flag for runtime detection of openssl usage */
284
285/** Error buffer for cURL. */
286static char fetch_error_buffer[CURL_ERROR_SIZE];
287
288/** Proxy authentication details. */
289static char fetch_proxy_userpwd[100];
290
291/** Interlock to prevent initiation during callbacks */
292static bool inside_curl = false;
293
294
295/**
296 * Initialise a cURL fetcher.
297 */
298static bool fetch_curl_initialise(lwc_string *scheme)
299{
300 NSLOG(netsurf, INFO, "Initialise cURL fetcher for %s",
301 lwc_string_data(scheme));
303 return true; /* Always succeeds */
304}
305
306
307/**
308 * Finalise a cURL fetcher.
309 *
310 * \param scheme The scheme to finalise.
311 */
312static void fetch_curl_finalise(lwc_string *scheme)
313{
314 struct cache_handle *h;
315
317 NSLOG(netsurf, INFO, "Finalise cURL fetcher %s",
318 lwc_string_data(scheme));
319 if (curl_fetchers_registered == 0) {
320 CURLMcode codem;
321 /* All the fetchers have been finalised. */
322 NSLOG(netsurf, INFO,
323 "All cURL fetchers finalised, closing down cURL");
324
325 curl_easy_cleanup(fetch_blank_curl);
326
327 codem = curl_multi_cleanup(fetch_curl_multi);
328 if (codem != CURLM_OK)
329 NSLOG(netsurf, INFO,
330 "curl_multi_cleanup failed: ignoring");
331
332 curl_global_cleanup();
333
334 NSLOG(netsurf, DEBUG, "Cleaning up SSL cert chain hashmap");
337 }
338
339 /* Free anything remaining in the cached curl handle ring */
340 while (curl_handle_ring != NULL) {
343 lwc_string_unref(h->host);
344 curl_easy_cleanup(h->handle);
345 free(h);
346 }
347}
348
349
350/**
351 * Check if this fetcher can fetch a url.
352 *
353 * \param url The url to check.
354 * \return true if the fetcher supports the url else false.
355 */
356static bool fetch_curl_can_fetch(const nsurl *url)
357{
358 return nsurl_has_component(url, NSURL_HOST);
359}
360
361
362
363/**
364 * allocate postdata
365 */
366static struct fetch_postdata *
367fetch_curl_alloc_postdata(const char *post_urlenc,
368 const struct fetch_multipart_data *post_multipart)
369{
370 struct fetch_postdata *postdata;
371 postdata = calloc(1, sizeof(struct fetch_postdata));
372 if (postdata != NULL) {
373
374 if (post_urlenc) {
375 postdata->type = FETCH_POSTDATA_URLENC;
376 postdata->data.urlenc = strdup(post_urlenc);
377 if (postdata->data.urlenc == NULL) {
378 free(postdata);
379 postdata = NULL;
380 }
381 } else if (post_multipart) {
382 postdata->type = FETCH_POSTDATA_MULTIPART;
383 postdata->data.multipart = fetch_multipart_data_clone(post_multipart);
384 if (postdata->data.multipart == NULL) {
385 free(postdata);
386 postdata = NULL;
387 }
388 } else {
389 postdata->type = FETCH_POSTDATA_NONE;
390 }
391 }
392 return postdata;
393}
394
395/**
396 * free postdata
397 */
398static void fetch_curl_free_postdata(struct fetch_postdata *postdata)
399{
400 if (postdata != NULL) {
401 switch (postdata->type) {
403 break;
405 free(postdata->data.urlenc);
406 break;
409 break;
410 }
411
412 free(postdata);
413 }
414}
415
416/**
417 *construct a new fetch structure
418 */
419static struct curl_fetch_info *fetch_alloc(void)
420{
421 struct curl_fetch_info *fetch;
422 fetch = malloc(sizeof (*fetch));
423 if (fetch == NULL)
424 return NULL;
425
426 fetch->curl_handle = NULL;
427 fetch->sent_ssl_chain = false;
428 fetch->had_headers = false;
429 fetch->abort = false;
430 fetch->stopped = false;
431 fetch->only_2xx = false;
432 fetch->downgrade_tls = false;
433 fetch->headers = NULL;
434 fetch->url = NULL;
435 fetch->host = NULL;
436 fetch->location = NULL;
437 fetch->content_length = 0;
438 fetch->http_code = 0;
439 fetch->cookie_string = NULL;
440 fetch->realm = NULL;
441 fetch->last_progress_update = 0;
442 fetch->postdata = NULL;
443 fetch->curl_postdata = NULL;
444
445 /* Clear certificate chain data */
446 memset(fetch->cert_data, 0, sizeof(fetch->cert_data));
447 fetch->cert_depth = -1;
448
449 return fetch;
450}
451
452/**
453 * Start fetching data for the given URL.
454 *
455 * The function returns immediately. The fetch may be queued for later
456 * processing.
457 *
458 * A pointer to an opaque struct curl_fetch_info is returned, which can be
459 * passed to fetch_abort() to abort the fetch at any time. Returns 0 if memory
460 * is exhausted (or some other fatal error occurred).
461 *
462 * The caller must supply a callback function which is called when anything
463 * interesting happens. The callback function is first called with msg
464 * FETCH_HEADER, with the header in data, then one or more times
465 * with FETCH_DATA with some data for the url, and finally with
466 * FETCH_FINISHED. Alternatively, FETCH_ERROR indicates an error occurred:
467 * data contains an error message. FETCH_REDIRECT may replace the FETCH_HEADER,
468 * FETCH_DATA, FETCH_FINISHED sequence if the server sends a replacement URL.
469 *
470 * Some private data can be passed as the last parameter to fetch_start, and
471 * callbacks will contain this.
472 */
473static void *
474fetch_curl_setup(struct fetch *parent_fetch,
475 nsurl *url,
476 bool only_2xx,
477 bool downgrade_tls,
478 const char *post_urlenc,
479 const struct fetch_multipart_data *post_multipart,
480 const char **headers)
481{
482 struct curl_fetch_info *fetch;
483 struct curl_slist *slist;
484 int i;
485
486 fetch = fetch_alloc();
487 if (fetch == NULL)
488 return NULL;
489
490 NSLOG(netsurf, INFO, "fetch %p, url '%s'", fetch, nsurl_access(url));
491
492 fetch->only_2xx = only_2xx;
493 fetch->downgrade_tls = downgrade_tls;
494 fetch->fetch_handle = parent_fetch;
495 fetch->url = nsurl_ref(url);
497 if (fetch->host == NULL) {
498 goto failed;
499 }
500 fetch->postdata = fetch_curl_alloc_postdata(post_urlenc, post_multipart);
501 if (fetch->postdata == NULL) {
502 goto failed;
503 }
504
505#define APPEND(list, value) \
506 slist = curl_slist_append(list, value); \
507 if (slist == NULL) \
508 goto failed; \
509 list = slist;
510
511 /* remove curl default headers */
512 APPEND(fetch->headers, "Pragma:");
513
514 /* when doing a POST libcurl sends Expect: 100-continue" by default
515 * which fails with lighttpd, so disable it (see bug 1429054) */
516 APPEND(fetch->headers, "Expect:");
517
518 if ((nsoption_charp(accept_language) != NULL) &&
519 (nsoption_charp(accept_language)[0] != '\0')) {
520 char s[80];
521 snprintf(s, sizeof s, "Accept-Language: %s, *;q=0.1",
522 nsoption_charp(accept_language));
523 s[sizeof s - 1] = 0;
524 APPEND(fetch->headers, s);
525 }
526
527 if (nsoption_charp(accept_charset) != NULL &&
528 nsoption_charp(accept_charset)[0] != '\0') {
529 char s[80];
530 snprintf(s, sizeof s, "Accept-Charset: %s, *;q=0.1",
531 nsoption_charp(accept_charset));
532 s[sizeof s - 1] = 0;
533 APPEND(fetch->headers, s);
534 }
535
536 if (nsoption_bool(do_not_track) == true) {
537 APPEND(fetch->headers, "DNT: 1");
538 }
539
540 /* And add any headers specified by the caller */
541 for (i = 0; headers[i] != NULL; i++) {
542 APPEND(fetch->headers, headers[i]);
543 }
544
545 return fetch;
546
547#undef APPEND
548
549failed:
550 lwc_string_unref(fetch->host);
553 curl_slist_free_all(fetch->headers);
554 free(fetch);
555 return NULL;
556}
557
558
559#ifdef WITH_OPENSSL
560
561/**
562 * Retrieve the ssl cert chain for the fetch, creating a blank one if needed
563 */
564static struct cert_chain *
565fetch_curl_get_cached_chain(struct curl_fetch_info *f)
566{
567 struct cert_chain *chain;
568
570 if (chain == NULL) {
572 }
573
574 return chain;
575}
576
577/**
578 * Report the certificate information in the fetch to the users
579 */
580static void
581fetch_curl_store_certs_in_cache(struct curl_fetch_info *f)
582{
583 size_t depth;
584 BIO *mem;
585 BUF_MEM *buf[MAX_CERT_DEPTH];
586 struct cert_chain chain, *cached_chain;
587 struct cert_info *certs;
588
589 memset(&chain, 0, sizeof(chain));
590
591 certs = f->cert_data;
592 chain.depth = f->cert_depth + 1; /* 0 indexed certificate depth */
593
594 for (depth = 0; depth < chain.depth; depth++) {
595 if (certs[depth].cert == NULL) {
596 /* This certificate is missing, skip it */
597 chain.certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
598 continue;
599 }
600
601 /* error code (if any) */
602 switch (certs[depth].err) {
603 case X509_V_OK:
604 chain.certs[depth].err = SSL_CERT_ERR_OK;
605 break;
606
607 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
608 /* fallthrough */
609 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
610 chain.certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
611 break;
612
613 case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
614 /* fallthrough */
615 case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
616 /* fallthrough */
617 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
618 /* fallthrough */
619 case X509_V_ERR_CRL_SIGNATURE_FAILURE:
620 chain.certs[depth].err = SSL_CERT_ERR_BAD_SIG;
621 break;
622
623 case X509_V_ERR_CERT_NOT_YET_VALID:
624 /* fallthrough */
625 case X509_V_ERR_CRL_NOT_YET_VALID:
626 chain.certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
627 break;
628
629 case X509_V_ERR_CERT_HAS_EXPIRED:
630 /* fallthrough */
631 case X509_V_ERR_CRL_HAS_EXPIRED:
632 chain.certs[depth].err = SSL_CERT_ERR_TOO_OLD;
633 break;
634
635 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
636 chain.certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
637 break;
638
639 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
641 break;
642
643 case X509_V_ERR_CERT_REVOKED:
644 chain.certs[depth].err = SSL_CERT_ERR_REVOKED;
645 break;
646
647 case X509_V_ERR_HOSTNAME_MISMATCH:
649 break;
650
651 default:
652 chain.certs[depth].err = SSL_CERT_ERR_UNKNOWN;
653 break;
654 }
655
656 /*
657 * get certificate in Distinguished Encoding Rules (DER) format.
658 */
659 mem = BIO_new(BIO_s_mem());
660 i2d_X509_bio(mem, certs[depth].cert);
661 BIO_get_mem_ptr(mem, &buf[depth]);
662 (void) BIO_set_close(mem, BIO_NOCLOSE);
663 BIO_free(mem);
664
665 chain.certs[depth].der = (uint8_t *)buf[depth]->data;
666 chain.certs[depth].der_length = buf[depth]->length;
667 }
668
669 /* Now dup that chain into the cache */
670 cached_chain = fetch_curl_get_cached_chain(f);
671 if (cert_chain_dup_into(&chain, cached_chain) != NSERROR_OK) {
672 /* Something went wrong storing the chain, give up */
674 }
675
676 /* release the openssl memory buffer */
677 for (depth = 0; depth < chain.depth; depth++) {
678 if (chain.certs[depth].err == SSL_CERT_ERR_CERT_MISSING) {
679 continue;
680 }
681 if (buf[depth] != NULL) {
682 BUF_MEM_free(buf[depth]);
683 }
684 }
685}
686
687/**
688 * OpenSSL Certificate verification callback
689 *
690 * Called for each certificate in a chain being verified. OpenSSL
691 * calls this in deepest first order from the certificate authority to
692 * the peer certificate at position 0.
693 *
694 * Each certificate is stored in the fetch context the first time it
695 * is presented. If an error is encountered it is only returned for
696 * the peer certificate at position 0 allowing the enumeration of the
697 * entire chain not stopping early at the depth of the erroring
698 * certificate.
699 *
700 * \param verify_ok 0 if the caller has already determined the chain
701 * has errors else 1
702 * \param x509_ctx certificate context being verified
703 * \return 1 to indicate verification should continue and 0 to indicate
704 * verification should stop.
705 */
706static int
707fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx)
708{
709 int depth;
710 struct curl_fetch_info *fetch;
711
712 depth = X509_STORE_CTX_get_error_depth(x509_ctx);
713 fetch = X509_STORE_CTX_get_app_data(x509_ctx);
714
715 /* certificate chain is excessively deep so fail verification */
716 if (depth >= MAX_CERT_DEPTH) {
717 X509_STORE_CTX_set_error(x509_ctx,
718 X509_V_ERR_CERT_CHAIN_TOO_LONG);
719 return 0;
720 }
721
722 /* record the max depth */
723 if (depth > fetch->cert_depth) {
724 fetch->cert_depth = depth;
725 }
726
727 /* save the certificate by incrementing the reference count and
728 * keeping a pointer.
729 */
730 if (!fetch->cert_data[depth].cert) {
731 fetch->cert_data[depth].cert = X509_STORE_CTX_get_current_cert(x509_ctx);
732 X509_up_ref(fetch->cert_data[depth].cert);
733 fetch->cert_data[depth].err = X509_STORE_CTX_get_error(x509_ctx);
734 }
735
736 /* allow certificate chain to be completed */
737 if (depth > 0) {
738 verify_ok = 1;
739 } else {
740 /* search for deeper certificates in the chain with errors */
741 for (depth = fetch->cert_depth; depth > 0; depth--) {
742 if (fetch->cert_data[depth].err != 0) {
743 /* error in previous certificate so fail verification */
744 verify_ok = 0;
745 X509_STORE_CTX_set_error(x509_ctx, fetch->cert_data[depth].err);
746 }
747 }
748 }
749
750 return verify_ok;
751}
752
753
754/**
755 * OpenSSL certificate chain verification callback
756 *
757 * Verifies certificate chain by calling standard implementation after
758 * setting up context for the certificate callback.
759 *
760 * \param x509_ctx The certificate store to validate
761 * \param parm The fetch context.
762 * \return 1 to indicate verification success and 0 to indicate verification failure.
763 */
764static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm)
765{
766 struct curl_fetch_info *f = (struct curl_fetch_info *) parm;
767 int ok;
768 X509_VERIFY_PARAM *vparam;
769
770 /* Configure the verification parameters to include hostname */
771 vparam = X509_STORE_CTX_get0_param(x509_ctx);
772 X509_VERIFY_PARAM_set_hostflags(vparam, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
773
774 ok = X509_VERIFY_PARAM_set1_host(vparam,
775 lwc_string_data(f->host),
776 lwc_string_length(f->host));
777
778 /* Store fetch struct in context for verify callback */
779 if (ok) {
780 ok = X509_STORE_CTX_set_app_data(x509_ctx, parm);
781 }
782
783 /* verify the certificate chain using standard call */
784 if (ok) {
785 ok = X509_verify_cert(x509_ctx);
786 }
787
788 fetch_curl_store_certs_in_cache(f);
789
790 return ok;
791}
792
793
794/**
795 * cURL SSL setup callback
796 *
797 * \param curl_handle The curl handle to perform the ssl operation on.
798 * \param _sslctx The ssl context.
799 * \param parm The callback context.
800 * \return A curl result code.
801 */
802static CURLcode
803fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
804{
805 struct curl_fetch_info *f = (struct curl_fetch_info *) parm;
806 SSL_CTX *sslctx = _sslctx;
807 long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
808 SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
809
810 /* set verify callback for each certificate in chain */
811 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback);
812
813 /* set callback used to verify certificate chain */
814 SSL_CTX_set_cert_verify_callback(sslctx,
815 fetch_curl_cert_verify_callback,
816 parm);
817
818 if (f->downgrade_tls) {
819 /* Disable TLS 1.3 if the server can't cope with it */
820#ifdef SSL_OP_NO_TLSv1_3
821 options |= SSL_OP_NO_TLSv1_3;
822#endif
823#ifdef SSL_MODE_SEND_FALLBACK_SCSV
824 /* Ensure server rejects the connection if downgraded too far */
825 SSL_CTX_set_mode(sslctx, SSL_MODE_SEND_FALLBACK_SCSV);
826#endif
827 }
828
829 SSL_CTX_set_options(sslctx, options);
830
831#ifdef SSL_OP_NO_TICKET
832 SSL_CTX_clear_options(sslctx, SSL_OP_NO_TICKET);
833#endif
834
835 return CURLE_OK;
836}
837
838
839#endif /* WITH_OPENSSL */
840
841
842/**
843 * Report the certificate information in the fetch to the users
844 */
845static void
847{
848 fetch_msg msg;
849 struct cert_chain *chain;
850
852
853 if (chain != NULL) {
854 msg.type = FETCH_CERTS;
855 msg.data.chain = chain;
856
858 }
859
860 f->sent_ssl_chain = true;
861}
862
863#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 depricated curl_formadd */
864
865/**
866 * curl mime data context
867 */
868struct curl_mime_ctx {
869 char *buffer;
870 curl_off_t size;
871 curl_off_t position;
872};
873
874static size_t mime_data_read_callback(char *buffer, size_t size, size_t nitems, void *arg)
875{
876 struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
877 curl_off_t sz = mctx->size - mctx->position;
878
879 nitems *= size;
880 if(sz > (curl_off_t)nitems) {
881 sz = nitems;
882 }
883 if(sz) {
884 memcpy(buffer, mctx->buffer + mctx->position, sz);
885 }
886 mctx->position += sz;
887 return sz;
888}
889
890static int mime_data_seek_callback(void *arg, curl_off_t offset, int origin)
891{
892 struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
893
894 switch(origin) {
895 case SEEK_END:
896 offset += mctx->size;
897 break;
898 case SEEK_CUR:
899 offset += mctx->position;
900 break;
901 }
902
903 if(offset < 0) {
904 return CURL_SEEKFUNC_FAIL;
905 }
906 mctx->position = offset;
907 return CURL_SEEKFUNC_OK;
908}
909
910static void mime_data_free_callback(void *arg)
911{
912 struct curl_mime_ctx *mctx = (struct curl_mime_ctx *) arg;
913 free(mctx);
914}
915
916/**
917 * Convert a POST data list to a libcurl curl_mime.
918 *
919 * \param chandle curl fetch handle.
920 * \param multipart limked list of struct ::fetch_multipart forming post data.
921 */
922static curl_mime *
923fetch_curl_postdata_convert(CURL *chandle,
924 const struct fetch_multipart_data *multipart)
925{
926 curl_mime *cmime;
927 curl_mimepart *part;
928 CURLcode code = CURLE_OK;
929 size_t value_len;
930
931 cmime = curl_mime_init(chandle);
932 if (cmime == NULL) {
933 NSLOG(netsurf, WARNING, "postdata conversion failed to curl mime context");
934 return NULL;
935 }
936
937 /* iterate post data */
938 for (; multipart != NULL; multipart = multipart->next) {
939 part = curl_mime_addpart(cmime);
940 if (part == NULL) {
941 goto convert_failed;
942 }
943
944 code = curl_mime_name(part, multipart->name);
945 if (code != CURLE_OK) {
946 goto convert_failed;
947 }
948
949 value_len = strlen(multipart->value);
950
951 if (multipart->file && value_len==0) {
952 /* file entries with no filename require special handling */
953 code=curl_mime_data(part, multipart->value, value_len);
954 if (code != CURLE_OK) {
955 goto convert_failed;
956 }
957
958 code = curl_mime_filename(part, "");
959 if (code != CURLE_OK) {
960 goto convert_failed;
961 }
962
963 code = curl_mime_type(part, "application/octet-stream");
964 if (code != CURLE_OK) {
965 goto convert_failed;
966 }
967
968 } else if(multipart->file) {
969 /* file entry */
970 nserror ret;
971 char *leafname = NULL;
972 char *mimetype = NULL;
973
974 code = curl_mime_filedata(part, multipart->rawfile);
975 if (code != CURLE_OK) {
976 goto convert_failed;
977 }
978
979 ret = guit->file->basename(multipart->value, &leafname, NULL);
980 if (ret != NSERROR_OK) {
981 goto convert_failed;
982 }
983 code = curl_mime_filename(part, leafname);
984 free(leafname);
985 if (code != CURLE_OK) {
986 goto convert_failed;
987 }
988
989 mimetype = guit->fetch->mimetype(multipart->value);
990 if (mimetype == NULL) {
991 mimetype=strdup("text/plain");
992 }
993 if (mimetype == NULL) {
994 goto convert_failed;
995 }
996 code = curl_mime_type(part, mimetype);
997 free(mimetype);
998 if (code != CURLE_OK) {
999 goto convert_failed;
1000 }
1001
1002 } else {
1003 /* make the curl mime reference the existing multipart
1004 * data which requires use of a callback and context.
1005 */
1006 struct curl_mime_ctx *cb_ctx;
1007 cb_ctx = malloc(sizeof(struct curl_mime_ctx));
1008 if (cb_ctx == NULL) {
1009 goto convert_failed;
1010 }
1011 cb_ctx->buffer = multipart->value;
1012 cb_ctx->size = value_len;
1013 cb_ctx->position = 0;
1014 code = curl_mime_data_cb(part,
1015 value_len,
1016 mime_data_read_callback,
1017 mime_data_seek_callback,
1018 mime_data_free_callback,
1019 cb_ctx);
1020 if (code != CURLE_OK) {
1021 free(cb_ctx);
1022 goto convert_failed;
1023 }
1024 }
1025 }
1026
1027 return cmime;
1028
1029convert_failed:
1030 NSLOG(netsurf, WARNING, "postdata conversion failed with curl code: %d", code);
1031 curl_mime_free(cmime);
1032 return NULL;
1033}
1034
1035#else /* LIBCURL_VERSION_NUM >= 0x073800 */
1036
1037/**
1038 * Convert a list of struct ::fetch_multipart_data to a list of
1039 * struct curl_httppost for libcurl.
1040 */
1041static struct curl_httppost *
1043 const struct fetch_multipart_data *control)
1044{
1045 struct curl_httppost *post = NULL, *last = NULL;
1046 CURLFORMcode code;
1047 nserror ret;
1048
1049 for (; control; control = control->next) {
1050 if (control->file) {
1051 char *leafname = NULL;
1052 ret = guit->file->basename(control->value, &leafname, NULL);
1053 if (ret != NSERROR_OK) {
1054 continue;
1055 }
1056
1057 /* We have to special case filenames of "", so curl
1058 * a) actually attempts the fetch and
1059 * b) doesn't attempt to open the file ""
1060 */
1061 if (control->value[0] == '\0') {
1062 /* dummy buffer - needs to be static so
1063 * pointer's still valid when we go out
1064 * of scope (not that libcurl should be
1065 * attempting to access it, of course).
1066 */
1067 static char buf;
1068
1069 code = curl_formadd(&post, &last,
1070 CURLFORM_COPYNAME, control->name,
1071 CURLFORM_BUFFER, control->value,
1072 /* needed, as basename("") == "." */
1073 CURLFORM_FILENAME, "",
1074 CURLFORM_BUFFERPTR, &buf,
1075 CURLFORM_BUFFERLENGTH, 0,
1076 CURLFORM_CONTENTTYPE,
1077 "application/octet-stream",
1078 CURLFORM_END);
1079 if (code != CURL_FORMADD_OK)
1080 NSLOG(netsurf, INFO,
1081 "curl_formadd: %d (%s)", code,
1082 control->name);
1083 } else {
1084 char *mimetype = guit->fetch->mimetype(control->value);
1085 code = curl_formadd(&post, &last,
1086 CURLFORM_COPYNAME, control->name,
1087 CURLFORM_FILE, control->rawfile,
1088 CURLFORM_FILENAME, leafname,
1089 CURLFORM_CONTENTTYPE,
1090 (mimetype != 0 ? mimetype : "text/plain"),
1091 CURLFORM_END);
1092 if (code != CURL_FORMADD_OK)
1093 NSLOG(netsurf, INFO,
1094 "curl_formadd: %d (%s=%s)",
1095 code,
1096 control->name,
1097 control->value);
1098 free(mimetype);
1099 }
1100 free(leafname);
1101 } else {
1102 code = curl_formadd(&post, &last,
1103 CURLFORM_COPYNAME, control->name,
1104 CURLFORM_COPYCONTENTS, control->value,
1105 CURLFORM_END);
1106 if (code != CURL_FORMADD_OK)
1107 NSLOG(netsurf, INFO,
1108 "curl_formadd: %d (%s=%s)", code,
1109 control->name, control->value);
1110 }
1111 }
1112
1113 return post;
1114}
1115
1116#endif /* LIBCURL_VERSION_NUM >= 0x073800 */
1117
1118/**
1119 * Setup multipart post data
1120 */
1122{
1123 CURLcode code = CURLE_OK;
1124
1125#undef SETOPT
1126#define SETOPT(option, value) { \
1127 code = curl_easy_setopt(f->curl_handle, option, value); \
1128 if (code != CURLE_OK) \
1129 return code; \
1130 }
1131
1132 switch (f->postdata->type) {
1134 SETOPT(CURLOPT_POSTFIELDS, NULL);
1136 SETOPT(CURLOPT_HTTPGET, 1L);
1137 break;
1138
1141 SETOPT(CURLOPT_HTTPGET, 0L);
1142 SETOPT(CURLOPT_POSTFIELDS, f->postdata->data.urlenc);
1143 break;
1144
1146 SETOPT(CURLOPT_POSTFIELDS, NULL);
1147 SETOPT(CURLOPT_HTTPGET, 0L);
1148 if (f->curl_postdata == NULL) {
1149 f->curl_postdata =
1151 f->postdata->data.multipart);
1152 }
1154 break;
1155 }
1156 return code;
1157}
1158
1159/**
1160 * Set options specific for a fetch.
1161 *
1162 * \param f The fetch to set options on.
1163 * \return A curl result code.
1164 */
1165static CURLcode fetch_curl_set_options(struct curl_fetch_info *f)
1166{
1167 CURLcode code;
1168 const char *auth;
1169
1170#undef SETOPT
1171#define SETOPT(option, value) { \
1172 code = curl_easy_setopt(f->curl_handle, option, value); \
1173 if (code != CURLE_OK) \
1174 return code; \
1175 }
1176
1177 SETOPT(CURLOPT_URL, nsurl_access(f->url));
1178 SETOPT(CURLOPT_PRIVATE, f);
1179 SETOPT(CURLOPT_WRITEDATA, f);
1180 SETOPT(CURLOPT_WRITEHEADER, f);
1182 SETOPT(CURLOPT_HTTPHEADER, f->headers);
1183 code = fetch_curl_set_postdata(f);
1184 if (code != CURLE_OK) {
1185 return code;
1186 }
1187
1188 f->cookie_string = urldb_get_cookie(f->url, true);
1189 if (f->cookie_string) {
1190 SETOPT(CURLOPT_COOKIE, f->cookie_string);
1191 } else {
1192 SETOPT(CURLOPT_COOKIE, NULL);
1193 }
1194
1195 if ((auth = urldb_get_auth_details(f->url, NULL)) != NULL) {
1196 SETOPT(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
1197 SETOPT(CURLOPT_USERPWD, auth);
1198 } else {
1199 SETOPT(CURLOPT_USERPWD, NULL);
1200 }
1201
1202 /* set up proxy options */
1203 if (nsoption_bool(http_proxy) &&
1204 (nsoption_charp(http_proxy_host) != NULL) &&
1205 (strncmp(nsurl_access(f->url), "file:", 5) != 0)) {
1206 SETOPT(CURLOPT_PROXY, nsoption_charp(http_proxy_host));
1207 SETOPT(CURLOPT_PROXYPORT, (long) nsoption_int(http_proxy_port));
1208
1209#if LIBCURL_VERSION_NUM >= 0x071304
1210 /* Added in 7.19.4 */
1211 /* setup the omission list */
1212 SETOPT(CURLOPT_NOPROXY, nsoption_charp(http_proxy_noproxy));
1213#endif
1214
1215 if (nsoption_int(http_proxy_auth) != OPTION_HTTP_PROXY_AUTH_NONE) {
1216 SETOPT(CURLOPT_PROXYAUTH,
1217 nsoption_int(http_proxy_auth) ==
1219 (long) CURLAUTH_BASIC :
1220 (long) CURLAUTH_NTLM);
1221 snprintf(fetch_proxy_userpwd,
1222 sizeof fetch_proxy_userpwd,
1223 "%s:%s",
1224 nsoption_charp(http_proxy_auth_user),
1225 nsoption_charp(http_proxy_auth_pass));
1226 SETOPT(CURLOPT_PROXYUSERPWD, fetch_proxy_userpwd);
1227 }
1228 } else {
1229 SETOPT(CURLOPT_PROXY, NULL);
1230 }
1231
1232
1233 if (curl_with_openssl) {
1234 SETOPT(CURLOPT_SSL_CIPHER_LIST,
1236 }
1237
1238 /* Force-enable SSL session ID caching, as some distros are odd. */
1239 SETOPT(CURLOPT_SSL_SESSIONID_CACHE, 1L);
1240
1242 /* Disable certificate verification */
1243 SETOPT(CURLOPT_SSL_VERIFYPEER, 0L);
1244 SETOPT(CURLOPT_SSL_VERIFYHOST, 0L);
1245 if (curl_with_openssl) {
1246 SETOPT(CURLOPT_SSL_CTX_FUNCTION, NULL);
1247 SETOPT(CURLOPT_SSL_CTX_DATA, NULL);
1248 }
1249 } else {
1250 /* do verification */
1251 SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
1252 SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
1253#ifdef WITH_OPENSSL
1254 if (curl_with_openssl) {
1255 SETOPT(CURLOPT_SSL_CTX_FUNCTION, fetch_curl_sslctxfun);
1256 SETOPT(CURLOPT_SSL_CTX_DATA, f);
1257 }
1258#endif
1259 }
1260
1261 return CURLE_OK;
1262}
1263
1264/**
1265 * Initiate a fetch from the queue.
1266 *
1267 * \param fetch fetch to use to fetch content.
1268 * \param handle CURL handle to be used to fetch the content.
1269 * \return true if the fetch was successfully initiated else false.
1270 */
1271static bool
1273{
1274 CURLcode code;
1275 CURLMcode codem;
1276
1277 fetch->curl_handle = handle;
1278
1279 /* Initialise the handle */
1281 if (code != CURLE_OK) {
1282 fetch->curl_handle = 0;
1283 /* The handle maybe went bad, eat it */
1284 NSLOG(netsurf, WARNING, "cURL handle maybe went bad, retry later");
1285 curl_easy_cleanup(handle);
1286 return false;
1287 }
1288
1289 /* add to the global curl multi handle */
1290 codem = curl_multi_add_handle(fetch_curl_multi, fetch->curl_handle);
1291 assert(codem == CURLM_OK || codem == CURLM_CALL_MULTI_PERFORM);
1292
1293 return true;
1294}
1295
1296
1297/**
1298 * Find a CURL handle to use to dispatch a job
1299 */
1300static CURL *fetch_curl_get_handle(lwc_string *host)
1301{
1302 struct cache_handle *h;
1303 CURL *ret;
1305 if (h) {
1306 ret = h->handle;
1307 lwc_string_unref(h->host);
1309 free(h);
1310 } else {
1311 ret = curl_easy_duphandle(fetch_blank_curl);
1312 }
1313 return ret;
1314}
1315
1316
1317/**
1318 * Dispatch a single job
1319 */
1320static bool fetch_curl_start(void *vfetch)
1321{
1322 struct curl_fetch_info *fetch = (struct curl_fetch_info*)vfetch;
1323 if (inside_curl) {
1324 NSLOG(netsurf, DEBUG, "Deferring fetch because we're inside cURL");
1325 return false;
1326 }
1329}
1330
1331/**
1332 * Cache a CURL handle for the provided host (if wanted)
1333 */
1334static void fetch_curl_cache_handle(CURL *handle, lwc_string *host)
1335{
1336#if LIBCURL_VERSION_NUM >= 0x071e00
1337 /* 7.30.0 or later has its own connection caching; suppress ours */
1338 curl_easy_cleanup(handle);
1339 return;
1340#else
1341 struct cache_handle *h = 0;
1342 int c;
1344 if (h) {
1345 /* Already have a handle cached for this hostname */
1346 curl_easy_cleanup(handle);
1347 return;
1348 }
1349 /* We do not have a handle cached, first up determine if the cache is full */
1351 if (c >= nsoption_int(max_cached_fetch_handles)) {
1352 /* Cache is full, so, we rotate the ring by one and
1353 * replace the oldest handle with this one. We do this
1354 * without freeing/allocating memory (except the
1355 * hostname) and without removing the entry from the
1356 * ring and then re-inserting it, in order to be as
1357 * efficient as we can.
1358 */
1359 if (curl_handle_ring != NULL) {
1360 h = curl_handle_ring;
1362 curl_easy_cleanup(h->handle);
1363 h->handle = handle;
1364 lwc_string_unref(h->host);
1365 h->host = lwc_string_ref(host);
1366 } else {
1367 /* Actually, we don't want to cache any handles */
1368 curl_easy_cleanup(handle);
1369 }
1370
1371 return;
1372 }
1373 /* The table isn't full yet, so make a shiny new handle to add to the ring */
1374 h = (struct cache_handle*)malloc(sizeof(struct cache_handle));
1375 h->handle = handle;
1376 h->host = lwc_string_ref(host);
1378#endif
1379}
1380
1381
1382/**
1383 * Clean up the provided fetch object and free it.
1384 *
1385 * Will prod the queue afterwards to allow pending requests to be initiated.
1386 */
1387static void fetch_curl_stop(struct curl_fetch_info *f)
1388{
1389 CURLMcode codem;
1390
1391 assert(f);
1392 NSLOG(netsurf, INFO, "fetch %p, url '%s'", f, nsurl_access(f->url));
1393
1394 if (f->curl_handle) {
1395 /* remove from curl multi handle */
1396 codem = curl_multi_remove_handle(fetch_curl_multi,
1397 f->curl_handle);
1398 assert(codem == CURLM_OK);
1399 /* Put this curl handle into the cache if wanted. */
1401 f->curl_handle = 0;
1402 }
1403
1405}
1406
1407
1408/**
1409 * Abort a fetch.
1410 */
1411static void fetch_curl_abort(void *vf)
1412{
1413 struct curl_fetch_info *f = (struct curl_fetch_info *)vf;
1414 assert(f);
1415 NSLOG(netsurf, INFO, "fetch %p, url '%s'", f, nsurl_access(f->url));
1416 if (f->curl_handle) {
1417 if (inside_curl) {
1418 NSLOG(netsurf, DEBUG, "Deferring cleanup");
1419 f->abort = true;
1420 } else {
1421 NSLOG(netsurf, DEBUG, "Immediate abort");
1422 fetch_curl_stop(f);
1424 }
1425 } else {
1428 }
1429}
1430
1431
1432/**
1433 * Free a fetch structure and associated resources.
1434 */
1435static void fetch_curl_free(void *vf)
1436{
1437 struct curl_fetch_info *f = (struct curl_fetch_info *)vf;
1438 int i;
1439
1440 if (f->curl_handle) {
1441 curl_easy_cleanup(f->curl_handle);
1442 }
1443 nsurl_unref(f->url);
1444 lwc_string_unref(f->host);
1445 free(f->location);
1446 free(f->cookie_string);
1447 free(f->realm);
1448 if (f->headers) {
1449 curl_slist_free_all(f->headers);
1450 }
1453
1454 /* free certificate data */
1455 for (i = 0; i < MAX_CERT_DEPTH; i++) {
1456 if (f->cert_data[i].cert != NULL) {
1457 X509_free(f->cert_data[i].cert);
1458 }
1459 }
1460
1461 free(f);
1462}
1463
1464
1465/**
1466 * Find the status code and content type and inform the caller.
1467 *
1468 * Return true if the fetch is being aborted.
1469 */
1471{
1472 long http_code;
1473 CURLcode code;
1474 fetch_msg msg;
1475
1476 f->had_headers = true;
1477
1478 if (!f->http_code) {
1479 code = curl_easy_getinfo(f->curl_handle, CURLINFO_HTTP_CODE,
1480 &f->http_code);
1482 assert(code == CURLE_OK);
1483 }
1484 http_code = f->http_code;
1485 NSLOG(netsurf, INFO, "HTTP status code %li", http_code);
1486
1487 if ((http_code == 304) && (f->postdata->type==FETCH_POSTDATA_NONE)) {
1488 /* Not Modified && GET request */
1489 msg.type = FETCH_NOTMODIFIED;
1491 return true;
1492 }
1493
1494 /* handle HTTP redirects (3xx response codes) */
1495 if (300 <= http_code && http_code < 400 && f->location != 0) {
1496 NSLOG(netsurf, INFO, "FETCH_REDIRECT, '%s'", f->location);
1497 msg.type = FETCH_REDIRECT;
1498 msg.data.redirect = f->location;
1500 return true;
1501 }
1502
1503 /* handle HTTP 401 (Authentication errors) */
1504 if (http_code == 401) {
1505 msg.type = FETCH_AUTH;
1506 msg.data.auth.realm = f->realm;
1508 return true;
1509 }
1510
1511 /* handle HTTP errors (non 2xx response codes) */
1512 if (f->only_2xx && strncmp(nsurl_access(f->url), "http", 4) == 0 &&
1513 (http_code < 200 || 299 < http_code)) {
1514 msg.type = FETCH_ERROR;
1515 msg.data.error = messages_get("Not2xx");
1517 return true;
1518 }
1519
1520 if (f->abort)
1521 return true;
1522
1523 return false;
1524}
1525
1526
1527/**
1528 * Handle a completed fetch (CURLMSG_DONE from curl_multi_info_read()).
1529 *
1530 * \param curl_handle curl easy handle of fetch
1531 * \param result The result code of the completed fetch.
1532 */
1533static void fetch_curl_done(CURL *curl_handle, CURLcode result)
1534{
1535 bool finished = false;
1536 bool error = false;
1537 bool cert = false;
1538 bool abort_fetch;
1539 struct curl_fetch_info *f;
1540 char **_hideous_hack = (char **) (void *) &f;
1541 CURLcode code;
1542
1543 /* find the structure associated with this fetch */
1544 /* For some reason, cURL thinks CURLINFO_PRIVATE should be a string?! */
1545 code = curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, _hideous_hack);
1546 assert(code == CURLE_OK);
1547
1548 abort_fetch = f->abort;
1549 NSLOG(netsurf, INFO, "done %s", nsurl_access(f->url));
1550
1551 if ((abort_fetch == false) &&
1552 (result == CURLE_OK ||
1553 ((result == CURLE_WRITE_ERROR) && (f->stopped == false)))) {
1554 /* fetch completed normally or the server fed us a junk gzip
1555 * stream (usually in the form of garbage at the end of the
1556 * stream). Curl will have fed us all but the last chunk of
1557 * decoded data, which is sad as, if we'd received the last
1558 * chunk, too, we'd be able to render the whole object.
1559 * As is, we'll just have to accept that the end of the
1560 * object will be truncated in this case and leave it to
1561 * the content handlers to cope.
1562 */
1563 if (f->stopped ||
1565 ; /* redirect with no body or similar */
1566 } else {
1567 finished = true;
1568 }
1569 } else if (result == CURLE_PARTIAL_FILE) {
1570 /* CURLE_PARTIAL_FILE occurs if the received body of a
1571 * response is smaller than that specified in the
1572 * Content-Length header.
1573 */
1575 ; /* redirect with partial body, or similar */
1576 else {
1577 finished = true;
1578 }
1579 } else if (result == CURLE_WRITE_ERROR && f->stopped) {
1580 /* CURLE_WRITE_ERROR occurs when fetch_curl_data
1581 * returns 0, which we use to abort intentionally
1582 */
1583 ;
1584 } else if (result == CURLE_SSL_PEER_CERTIFICATE ||
1585 result == CURLE_SSL_CACERT) {
1586 /* Some kind of failure has occurred. If we don't know
1587 * what happened, we'll have reported unknown errors up
1588 * to the user already via the certificate chain error fields.
1589 */
1590 cert = true;
1591 } else {
1592 NSLOG(netsurf, INFO, "Unknown cURL response code %d", result);
1593 error = true;
1594 }
1595
1596 fetch_curl_stop(f);
1597
1598 if (f->sent_ssl_chain == false) {
1600 }
1601
1602 if (abort_fetch) {
1603 ; /* fetch was aborted: no callback */
1604 } else if (finished) {
1605 fetch_msg msg;
1606 msg.type = FETCH_FINISHED;
1608 } else if (cert) {
1609 /* user needs to validate certificate with issue */
1610 fetch_msg msg;
1611 msg.type = FETCH_CERT_ERR;
1613 } else if (error) {
1614 fetch_msg msg;
1615 switch (result) {
1616 case CURLE_SSL_CONNECT_ERROR:
1617 msg.type = FETCH_SSL_ERR;
1618 break;
1619
1620 case CURLE_OPERATION_TIMEDOUT:
1621 msg.type = FETCH_TIMEDOUT;
1622 msg.data.error = curl_easy_strerror(result);
1623 break;
1624
1625 default:
1626 msg.type = FETCH_ERROR;
1627 msg.data.error = curl_easy_strerror(result);
1628 }
1629
1631 }
1632
1634}
1635
1636
1637/**
1638 * Do some work on current fetches.
1639 *
1640 * Must be called regularly to make progress on fetches.
1641 */
1642static void fetch_curl_poll(lwc_string *scheme_ignored)
1643{
1644 int running, queue;
1645 CURLMcode codem;
1646 CURLMsg *curl_msg;
1647
1648 if (nsoption_bool(suppress_curl_debug) == false) {
1649 fd_set read_fd_set, write_fd_set, exc_fd_set;
1650 int max_fd = -1;
1651 int i;
1652
1653 FD_ZERO(&read_fd_set);
1654 FD_ZERO(&write_fd_set);
1655 FD_ZERO(&exc_fd_set);
1656
1657 codem = curl_multi_fdset(fetch_curl_multi,
1658 &read_fd_set, &write_fd_set,
1659 &exc_fd_set, &max_fd);
1660 assert(codem == CURLM_OK);
1661
1662 NSLOG(netsurf, DEEPDEBUG,
1663 "Curl file descriptor states (maxfd=%i):", max_fd);
1664 for (i = 0; i <= max_fd; i++) {
1665 bool read = false;
1666 bool write = false;
1667 bool error = false;
1668
1669 if (FD_ISSET(i, &read_fd_set)) {
1670 read = true;
1671 }
1672 if (FD_ISSET(i, &write_fd_set)) {
1673 write = true;
1674 }
1675 if (FD_ISSET(i, &exc_fd_set)) {
1676 error = true;
1677 }
1678 if (read || write || error) {
1679 NSLOG(netsurf, DEEPDEBUG, " fd %i: %s %s %s", i,
1680 read ? "read" : " ",
1681 write ? "write" : " ",
1682 error ? "error" : " ");
1683 }
1684 }
1685 }
1686
1687 /* do any possible work on the current fetches */
1688 inside_curl = true;
1689 do {
1690 codem = curl_multi_perform(fetch_curl_multi, &running);
1691 if (codem != CURLM_OK && codem != CURLM_CALL_MULTI_PERFORM) {
1692 NSLOG(netsurf, WARNING,
1693 "curl_multi_perform: %i %s",
1694 codem, curl_multi_strerror(codem));
1695 return;
1696 }
1697 } while (codem == CURLM_CALL_MULTI_PERFORM);
1698
1699 /* process curl results */
1700 curl_msg = curl_multi_info_read(fetch_curl_multi, &queue);
1701 while (curl_msg) {
1702 switch (curl_msg->msg) {
1703 case CURLMSG_DONE:
1704 fetch_curl_done(curl_msg->easy_handle,
1705 curl_msg->data.result);
1706 break;
1707 default:
1708 break;
1709 }
1710 curl_msg = curl_multi_info_read(fetch_curl_multi, &queue);
1711 }
1712 inside_curl = false;
1713}
1714
1715
1716
1717
1718/**
1719 * Callback function for fetch progress.
1720 */
1721static int
1723 NSCURL_PROGRESS_T dltotal,
1724 NSCURL_PROGRESS_T dlnow,
1725 NSCURL_PROGRESS_T ultotal,
1726 NSCURL_PROGRESS_T ulnow)
1727{
1728 static char fetch_progress_buffer[256]; /**< Progress buffer for cURL */
1729 struct curl_fetch_info *f = (struct curl_fetch_info *) clientp;
1730 uint64_t time_now_ms;
1731 fetch_msg msg;
1732
1733 if (f->abort) {
1734 return 0;
1735 }
1736
1737 msg.type = FETCH_PROGRESS;
1738 msg.data.progress = fetch_progress_buffer;
1739
1740 /* Rate limit each fetch's progress notifications */
1741 nsu_getmonotonic_ms(&time_now_ms);
1742#define UPDATE_DELAY_MS (1000 / UPDATES_PER_SECOND)
1743 if (time_now_ms - f->last_progress_update < UPDATE_DELAY_MS) {
1744 return 0;
1745 }
1746#undef UPDATE_DELAY_MS
1747 f->last_progress_update = time_now_ms;
1748
1749 if (dltotal > 0) {
1750 snprintf(fetch_progress_buffer, 255,
1751 messages_get("Progress"),
1753 human_friendly_bytesize(dltotal));
1755 } else {
1756 snprintf(fetch_progress_buffer, 255,
1757 messages_get("ProgressU"),
1760 }
1761
1762 return 0;
1763}
1764
1765
1766/**
1767 * Format curl debug for nslog
1768 */
1769static int
1770fetch_curl_debug(CURL *handle,
1771 curl_infotype type,
1772 char *data,
1773 size_t size,
1774 void *userptr)
1775{
1776 static const char s_infotype[CURLINFO_END][3] = {
1777 "* ", "< ", "> ", "{ ", "} ", "{ ", "} "
1778 };
1779 switch(type) {
1780 case CURLINFO_TEXT:
1781 case CURLINFO_HEADER_OUT:
1782 case CURLINFO_HEADER_IN:
1783 NSLOG(fetch, DEBUG, "%s%.*s", s_infotype[type], (int)size - 1, data);
1784 break;
1785
1786 default:
1787 break;
1788 }
1789 return 0;
1790}
1791
1792
1793static curl_socket_t fetch_curl_socket_open(void *clientp,
1794 curlsocktype purpose, struct curl_sockaddr *address)
1795{
1796 (void) clientp;
1797 (void) purpose;
1798
1799 return (curl_socket_t) guit->fetch->socket_open(
1800 address->family, address->socktype,
1801 address->protocol);
1802}
1803
1804static int fetch_curl_socket_close(void *clientp, curl_socket_t item)
1805{
1806 (void) clientp;
1807
1808 return guit->fetch->socket_close((int) item);
1809}
1810
1811/**
1812 * Callback function for cURL.
1813 */
1814static size_t fetch_curl_data(char *data, size_t size, size_t nmemb, void *_f)
1815{
1816 struct curl_fetch_info *f = _f;
1817 CURLcode code;
1818 fetch_msg msg;
1819
1820 /* ensure we only have to get this information once */
1821 if (!f->http_code) {
1822 code = curl_easy_getinfo(f->curl_handle, CURLINFO_HTTP_CODE,
1823 &f->http_code);
1825 assert(code == CURLE_OK);
1826 }
1827
1828 /* ignore body if this is a 401 reply by skipping it and reset
1829 * the HTTP response code to enable follow up fetches.
1830 */
1831 if (f->http_code == 401) {
1832 f->http_code = 0;
1833 return size * nmemb;
1834 }
1835
1836 if (f->abort || (!f->had_headers && fetch_curl_process_headers(f))) {
1837 f->stopped = true;
1838 return 0;
1839 }
1840
1841 /* send data to the caller */
1842 msg.type = FETCH_DATA;
1843 msg.data.header_or_data.buf = (const uint8_t *) data;
1844 msg.data.header_or_data.len = size * nmemb;
1846
1847 if (f->abort) {
1848 f->stopped = true;
1849 return 0;
1850 }
1851
1852 return size * nmemb;
1853}
1854
1855
1856/**
1857 * Callback function for headers.
1858 *
1859 * See RFC 2616 4.2.
1860 */
1861static size_t
1862fetch_curl_header(char *data, size_t size, size_t nmemb, void *_f)
1863{
1864 struct curl_fetch_info *f = _f;
1865 int i;
1866 fetch_msg msg;
1867 size *= nmemb;
1868
1869 if (f->abort) {
1870 f->stopped = true;
1871 return 0;
1872 }
1873
1874 if (f->sent_ssl_chain == false) {
1876 }
1877
1878 msg.type = FETCH_HEADER;
1879 msg.data.header_or_data.buf = (const uint8_t *) data;
1880 msg.data.header_or_data.len = size;
1882
1883#define SKIP_ST(o) for (i = (o); i < (int) size && (data[i] == ' ' || data[i] == '\t'); i++)
1884
1885 if (12 < size && strncasecmp(data, "Location:", 9) == 0) {
1886 /* extract Location header */
1887 free(f->location);
1888 f->location = malloc(size);
1889 if (!f->location) {
1890 NSLOG(netsurf, INFO, "malloc failed");
1891 return size;
1892 }
1893 SKIP_ST(9);
1894 strncpy(f->location, data + i, size - i);
1895 f->location[size - i] = '\0';
1896 for (i = size - i - 1; i >= 0 &&
1897 (f->location[i] == ' ' ||
1898 f->location[i] == '\t' ||
1899 f->location[i] == '\r' ||
1900 f->location[i] == '\n'); i--)
1901 f->location[i] = '\0';
1902 } else if (15 < size && strncasecmp(data, "Content-Length:", 15) == 0) {
1903 /* extract Content-Length header */
1904 SKIP_ST(15);
1905 if (i < (int)size && '0' <= data[i] && data[i] <= '9')
1906 f->content_length = atol(data + i);
1907 } else if (17 < size && strncasecmp(data, "WWW-Authenticate:", 17) == 0) {
1908 /* extract the first Realm from WWW-Authenticate header */
1909 SKIP_ST(17);
1910
1911 while (i < (int) size - 5 &&
1912 strncasecmp(data + i, "realm", 5))
1913 i++;
1914 while (i < (int) size - 1 && data[++i] != '"')
1915 /* */;
1916 i++;
1917
1918 if (i < (int) size) {
1919 size_t end = i;
1920
1921 while (end < size && data[end] != '"')
1922 ++end;
1923
1924 if (end < size) {
1925 free(f->realm);
1926 f->realm = malloc(end - i + 1);
1927 if (f->realm != NULL) {
1928 strncpy(f->realm, data + i, end - i);
1929 f->realm[end - i] = '\0';
1930 }
1931 }
1932 }
1933 } else if (11 < size && strncasecmp(data, "Set-Cookie:", 11) == 0) {
1934 /* extract Set-Cookie header */
1935 SKIP_ST(11);
1936
1937 fetch_set_cookie(f->fetch_handle, &data[i]);
1938 }
1939
1940 return size;
1941#undef SKIP_ST
1942}
1943
1944static int fetch_curl_fdset(lwc_string *scheme, fd_set *read_set,
1945 fd_set *write_set, fd_set *error_set)
1946{
1947 CURLMcode code;
1948 int maxfd = -1;
1949
1950 code = curl_multi_fdset(fetch_curl_multi,
1951 read_set,
1952 write_set,
1953 error_set,
1954 &maxfd);
1955 assert(code == CURLM_OK);
1956
1957 return maxfd;
1958}
1959
1960
1961
1962/* exported function documented in content/fetchers/curl.h */
1964{
1965 CURLcode code;
1966 curl_version_info_data *data;
1967 int i;
1968 lwc_string *scheme;
1969 const struct fetcher_operation_table fetcher_ops = {
1971 .acceptable = fetch_curl_can_fetch,
1972 .setup = fetch_curl_setup,
1973 .start = fetch_curl_start,
1974 .abort = fetch_curl_abort,
1975 .free = fetch_curl_free,
1976 .poll = fetch_curl_poll,
1977 .fdset = fetch_curl_fdset,
1978 .finalise = fetch_curl_finalise
1979 };
1980
1981#if LIBCURL_VERSION_NUM >= 0x073800
1982 /* version 7.56.0 can select which SSL backend to use */
1983 CURLsslset setres;
1984
1985 setres = curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL);
1986 if (setres == CURLSSLSET_OK) {
1987 curl_with_openssl = true;
1988 } else {
1989 curl_with_openssl = false;
1990 }
1991#endif
1992
1993 NSLOG(netsurf, INFO, "curl_version %s", curl_version());
1994
1995 code = curl_global_init(CURL_GLOBAL_ALL);
1996 if (code != CURLE_OK) {
1997 NSLOG(netsurf, INFO, "curl_global_init failed.");
1998 return NSERROR_INIT_FAILED;
1999 }
2000
2001 fetch_curl_multi = curl_multi_init();
2002 if (!fetch_curl_multi) {
2003 NSLOG(netsurf, INFO, "curl_multi_init failed.");
2004 return NSERROR_INIT_FAILED;
2005 }
2006
2007#if LIBCURL_VERSION_NUM >= 0x071e00
2008 /* built against 7.30.0 or later: configure caching */
2009 {
2010 CURLMcode mcode;
2011 int maxconnects = nsoption_int(max_fetchers) +
2012 nsoption_int(max_cached_fetch_handles);
2013
2014#undef SETOPT
2015#define SETOPT(option, value) \
2016 mcode = curl_multi_setopt(fetch_curl_multi, option, value); \
2017 if (mcode != CURLM_OK) { \
2018 NSLOG(netsurf, ERROR, "attempting curl_multi_setopt(%s, ...)", #option); \
2019 goto curl_multi_setopt_failed; \
2020 }
2021
2022 SETOPT(CURLMOPT_MAXCONNECTS, maxconnects);
2023 SETOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, maxconnects);
2024 SETOPT(CURLMOPT_MAX_HOST_CONNECTIONS, nsoption_int(max_fetchers_per_host));
2025 }
2026#endif
2027
2028 /* Create a curl easy handle with the options that are common to all
2029 * fetches.
2030 */
2031 fetch_blank_curl = curl_easy_init();
2032 if (!fetch_blank_curl) {
2033 NSLOG(netsurf, INFO, "curl_easy_init failed");
2034 return NSERROR_INIT_FAILED;
2035 }
2036
2037#undef SETOPT
2038#define SETOPT(option, value) \
2039 code = curl_easy_setopt(fetch_blank_curl, option, value); \
2040 if (code != CURLE_OK) { \
2041 NSLOG(netsurf, ERROR, "attempting curl_easy_setopt(%s, ...)", #option); \
2042 goto curl_easy_setopt_failed; \
2043 }
2044
2045 SETOPT(CURLOPT_ERRORBUFFER, fetch_error_buffer);
2046 SETOPT(CURLOPT_DEBUGFUNCTION, fetch_curl_debug);
2047 if (nsoption_bool(suppress_curl_debug)) {
2048 SETOPT(CURLOPT_VERBOSE, 0);
2049 } else {
2050 SETOPT(CURLOPT_VERBOSE, 1);
2051 }
2052
2053 /* Currently we explode if curl uses HTTP2, so force 1.1. */
2054 SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
2055
2056 SETOPT(CURLOPT_WRITEFUNCTION, fetch_curl_data);
2057 SETOPT(CURLOPT_HEADERFUNCTION, fetch_curl_header);
2059 SETOPT(CURLOPT_NOPROGRESS, 0);
2060 SETOPT(CURLOPT_USERAGENT, user_agent_string());
2061 SETOPT(CURLOPT_ENCODING, "gzip");
2062 SETOPT(CURLOPT_LOW_SPEED_LIMIT, 1L);
2063 SETOPT(CURLOPT_LOW_SPEED_TIME, 180L);
2064 SETOPT(CURLOPT_NOSIGNAL, 1L);
2065 SETOPT(CURLOPT_CONNECTTIMEOUT, nsoption_uint(curl_fetch_timeout));
2066 SETOPT(CURLOPT_OPENSOCKETFUNCTION, fetch_curl_socket_open);
2067 SETOPT(CURLOPT_CLOSESOCKETFUNCTION, fetch_curl_socket_close);
2068
2069 if (nsoption_charp(ca_bundle) &&
2070 strcmp(nsoption_charp(ca_bundle), "")) {
2071 NSLOG(netsurf, INFO, "ca_bundle: '%s'",
2072 nsoption_charp(ca_bundle));
2073 SETOPT(CURLOPT_CAINFO, nsoption_charp(ca_bundle));
2074 }
2075 if (nsoption_charp(ca_path) && strcmp(nsoption_charp(ca_path), "")) {
2076 NSLOG(netsurf, INFO, "ca_path: '%s'", nsoption_charp(ca_path));
2077 SETOPT(CURLOPT_CAPATH, nsoption_charp(ca_path));
2078 }
2079
2080#if LIBCURL_VERSION_NUM < 0x073800
2081 /*
2082 * before 7.56.0 Detect openssl from whether the SSL CTX
2083 * function API works
2084 */
2085 code = curl_easy_setopt(fetch_blank_curl, CURLOPT_SSL_CTX_FUNCTION, NULL);
2086 if (code != CURLE_OK) {
2087 curl_with_openssl = false;
2088 } else {
2089 curl_with_openssl = true;
2090 }
2091#endif
2092
2093 if (curl_with_openssl) {
2094 /* only set the cipher list with openssl otherwise the
2095 * fetch fails with "Unknown cipher in list"
2096 */
2097#if LIBCURL_VERSION_NUM >= 0x073d00
2098 /* Need libcurl 7.61.0 or later built against OpenSSL with
2099 * TLS1.3 support */
2100 code = curl_easy_setopt(fetch_blank_curl,
2101 CURLOPT_TLS13_CIPHERS, CIPHER_SUITES);
2102 if (code != CURLE_OK && code != CURLE_NOT_BUILT_IN)
2103 goto curl_easy_setopt_failed;
2104#endif
2105 SETOPT(CURLOPT_SSL_CIPHER_LIST, CIPHER_LIST);
2106 }
2107
2108 NSLOG(netsurf, INFO, "cURL %slinked against openssl",
2109 curl_with_openssl ? "" : "not ");
2110
2111 /* cURL initialised okay, register the fetchers */
2112
2113 data = curl_version_info(CURLVERSION_NOW);
2114
2116 if (curl_fetch_ssl_hashmap == NULL) {
2117 NSLOG(netsurf, CRITICAL, "Unable to initialise SSL certificate hashmap");
2118 return NSERROR_NOMEM;
2119 }
2120
2121 for (i = 0; data->protocols[i]; i++) {
2122 if (strcmp(data->protocols[i], "http") == 0) {
2123 scheme = lwc_string_ref(corestring_lwc_http);
2124
2125 } else if (strcmp(data->protocols[i], "https") == 0) {
2126 scheme = lwc_string_ref(corestring_lwc_https);
2127
2128 } else {
2129 /* Ignore non-http(s) protocols */
2130 continue;
2131 }
2132
2133 if (fetcher_add(scheme, &fetcher_ops) != NSERROR_OK) {
2134 NSLOG(netsurf, INFO,
2135 "Unable to register cURL fetcher for %s",
2136 data->protocols[i]);
2137 }
2138 }
2139
2140 return NSERROR_OK;
2141
2142curl_easy_setopt_failed:
2143 NSLOG(netsurf, INFO, "curl_easy_setopt failed.");
2144 return NSERROR_INIT_FAILED;
2145
2146#if LIBCURL_VERSION_NUM >= 0x071e00
2147curl_multi_setopt_failed:
2148 NSLOG(netsurf, INFO, "curl_multi_setopt failed.");
2149 return NSERROR_INIT_FAILED;
2150#endif
2151}
STATIC char result[100]
Definition: arexx.c:77
static osspriteop_area * buffer
The buffer characteristics.
Definition: buffer.c:55
void fetch_set_http_code(struct fetch *fetch, long http_code)
set the http code of a fetch
Definition: fetch.c:790
void fetch_set_cookie(struct fetch *fetch, const char *data)
set cookie data on a fetch
Definition: fetch.c:799
nserror fetcher_add(lwc_string *scheme, const struct fetcher_operation_table *ops)
Register a fetcher for a scheme.
Definition: fetch.c:357
void fetch_send_callback(const fetch_msg *msg, struct fetch *fetch)
send message to fetch
Definition: fetch.c:753
void fetch_free(struct fetch *f)
Free a fetch structure and associated resources.
Definition: fetch.c:546
struct fetch_multipart_data * fetch_multipart_data_clone(const struct fetch_multipart_data *list)
Clone a linked list of fetch_multipart_data.
Definition: fetch.c:616
void fetch_remove_from_queues(struct fetch *fetch)
remove a queued fetch
Definition: fetch.c:763
void fetch_multipart_data_destroy(struct fetch_multipart_data *list)
Free a linked list of fetch_multipart_data.
Definition: fetch.c:697
Fetching of data from a URL (interface).
@ FETCH_POSTDATA_URLENC
Definition: fetch.h:101
@ FETCH_POSTDATA_NONE
Definition: fetch.h:100
@ FETCH_POSTDATA_MULTIPART
Definition: fetch.h:102
@ FETCH_REDIRECT
Definition: fetch.h:49
@ FETCH_DATA
Definition: fetch.h:44
@ FETCH_AUTH
Definition: fetch.h:51
@ FETCH_CERTS
Definition: fetch.h:42
@ FETCH_HEADER
Definition: fetch.h:43
@ FETCH_PROGRESS
Definition: fetch.h:41
@ FETCH_NOTMODIFIED
Definition: fetch.h:50
@ FETCH_FINISHED
Definition: fetch.h:46
@ FETCH_SSL_ERR
Definition: fetch.h:53
@ FETCH_ERROR
Definition: fetch.h:48
@ FETCH_TIMEDOUT
Definition: fetch.h:47
@ FETCH_CERT_ERR
Definition: fetch.h:52
Useful interned string pointers (interface).
#define CIPHER_SUITES
The ciphersuites the browser is prepared to use for TLS1.3.
Definition: curl.c:72
static char fetch_proxy_userpwd[100]
Proxy authentication details.
Definition: curl.c:289
static struct curl_fetch_info * fetch_alloc(void)
construct a new fetch structure
Definition: curl.c:419
static bool fetch_curl_process_headers(struct curl_fetch_info *f)
Find the status code and content type and inform the caller.
Definition: curl.c:1470
CURLM * fetch_curl_multi
Global cURL multi handle.
Definition: curl.c:271
static void curl_fetch_ssl_value_destroy(void *value)
Definition: curl.c:189
static size_t fetch_curl_header(char *data, size_t size, size_t nmemb, void *_f)
Callback function for headers.
Definition: curl.c:1862
static bool inside_curl
Interlock to prevent initiation during callbacks.
Definition: curl.c:292
#define NSCURL_PROGRESS_T
Definition: curl.c:221
static CURLcode fetch_curl_set_options(struct curl_fetch_info *f)
Set options specific for a fetch.
Definition: curl.c:1165
static hashmap_t * curl_fetch_ssl_hashmap
Definition: curl.c:206
static void * curl_fetch_ssl_value_alloc(void *key)
Definition: curl.c:177
#define NSCURL_POSTDATA_T
Definition: curl.c:229
#define UPDATE_DELAY_MS
static char fetch_error_buffer[CURL_ERROR_SIZE]
Error buffer for cURL.
Definition: curl.c:286
static void fetch_curl_free(void *vf)
Free a fetch structure and associated resources.
Definition: curl.c:1435
#define APPEND(list, value)
#define CIPHER_LIST
The ciphersuites the browser is prepared to use for TLS<1.3.
Definition: curl.c:80
static bool curl_with_openssl
Flag for runtime detection of openssl usage.
Definition: curl.c:283
static bool fetch_curl_initiate_fetch(struct curl_fetch_info *fetch, CURL *handle)
Initiate a fetch from the queue.
Definition: curl.c:1272
static int fetch_curl_socket_close(void *clientp, curl_socket_t item)
Definition: curl.c:1804
static CURL * fetch_curl_get_handle(lwc_string *host)
Find a CURL handle to use to dispatch a job.
Definition: curl.c:1300
char X509
Definition: curl.c:111
static void fetch_curl_cache_handle(CURL *handle, lwc_string *host)
Cache a CURL handle for the provided host (if wanted)
Definition: curl.c:1334
static void X509_free(X509 *cert)
Definition: curl.c:113
static void fetch_curl_free_postdata(struct fetch_postdata *postdata)
free postdata
Definition: curl.c:398
static int fetch_curl_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr)
Format curl debug for nslog.
Definition: curl.c:1770
static struct fetch_postdata * fetch_curl_alloc_postdata(const char *post_urlenc, const struct fetch_multipart_data *post_multipart)
allocate postdata
Definition: curl.c:367
#define NSCURLOPT_PROGRESS_DATA
Definition: curl.c:220
#define SKIP_ST(o)
static void fetch_curl_abort(void *vf)
Abort a fetch.
Definition: curl.c:1411
static struct curl_httppost * fetch_curl_postdata_convert(CURL *chandle, const struct fetch_multipart_data *control)
Convert a list of struct fetch_multipart_data to a list of struct curl_httppost for libcurl.
Definition: curl.c:1042
static hashmap_parameters_t curl_fetch_ssl_hashmap_parameters
Definition: curl.c:197
#define NSCURLOPT_PROGRESS_FUNCTION
Definition: curl.c:219
static bool fetch_curl_start(void *vfetch)
Dispatch a single job.
Definition: curl.c:1320
static void fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
Report the certificate information in the fetch to the users.
Definition: curl.c:846
static void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers)
Start fetching data for the given URL.
Definition: curl.c:474
static CURL * fetch_blank_curl
Curl handle with default options set; not used for transfers.
Definition: curl.c:274
static size_t fetch_curl_data(char *data, size_t size, size_t nmemb, void *_f)
Callback function for cURL.
Definition: curl.c:1814
static int curl_fetchers_registered
Count of how many schemes the curl fetcher is handling.
Definition: curl.c:280
nserror fetch_curl_register(void)
Register curl scheme handler.
Definition: curl.c:1963
static void fetch_curl_stop(struct curl_fetch_info *f)
Clean up the provided fetch object and free it.
Definition: curl.c:1387
static int fetch_curl_fdset(lwc_string *scheme, fd_set *read_set, fd_set *write_set, fd_set *error_set)
Definition: curl.c:1944
#define NSCURL_POSTDATA_FREE(x)
Definition: curl.c:231
static void fetch_curl_finalise(lwc_string *scheme)
Finalise a cURL fetcher.
Definition: curl.c:312
static bool fetch_curl_can_fetch(const nsurl *url)
Check if this fetcher can fetch a url.
Definition: curl.c:356
#define CIPHER_LIST_LEGACY
The legacy cipher suites the browser is prepared to use for TLS<1.3.
Definition: curl.c:95
static CURLcode fetch_curl_set_postdata(struct curl_fetch_info *f)
Setup multipart post data.
Definition: curl.c:1121
static int fetch_curl_progress(void *clientp, NSCURL_PROGRESS_T dltotal, NSCURL_PROGRESS_T dlnow, NSCURL_PROGRESS_T ultotal, NSCURL_PROGRESS_T ulnow)
Callback function for fetch progress.
Definition: curl.c:1722
static bool curl_fetch_ssl_key_eq(void *key1, void *key2)
Definition: curl.c:144
static bool fetch_curl_initialise(lwc_string *scheme)
Initialise a cURL fetcher.
Definition: curl.c:298
static struct cache_handle * curl_handle_ring
Ring of cached handles.
Definition: curl.c:277
static void fetch_curl_poll(lwc_string *scheme_ignored)
Do some work on current fetches.
Definition: curl.c:1642
#define SETOPT(option, value)
static void fetch_curl_done(CURL *curl_handle, CURLcode result)
Handle a completed fetch (CURLMSG_DONE from curl_multi_info_read()).
Definition: curl.c:1533
#define NSCURL_POSTDATA_CURLOPT
Definition: curl.c:230
static uint32_t curl_fetch_ssl_key_hash(void *key)
Definition: curl.c:124
static curl_socket_t fetch_curl_socket_open(void *clientp, curlsocktype purpose, struct curl_sockaddr *address)
Definition: curl.c:1793
Fetching of data from a URL (Registration).
char options[PATH_MAX]
Definition: gui.c:91
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_INIT_FAILED
Initialisation failed.
Definition: errors.h:38
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
Interface for fetchers factory.
const char * type
Definition: filetype.cpp:44
static nserror fetch(nsurl *url, enum backing_store_flags bsflags, uint8_t **data_out, size_t *datalen_out)
Retrieve an object from the backing store.
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
bool hashmap_remove(hashmap_t *hashmap, void *key)
Remove an entry from the hashmap.
Definition: hashmap.c:206
hashmap_t * hashmap_create(hashmap_parameters_t *params)
Create a hashmap.
Definition: hashmap.c:67
void * hashmap_lookup(hashmap_t *hashmap, void *key)
Look up a key in a hashmap.
Definition: hashmap.c:113
void * hashmap_insert(hashmap_t *hashmap, void *key)
Create an entry in a hashmap.
Definition: hashmap.c:131
void hashmap_destroy(hashmap_t *hashmap)
Destroy a hashmap.
Definition: hashmap.c:91
void(* hashmap_key_destroy_t)(void *)
Key destructor function type.
Definition: hashmap.h:42
void *(* hashmap_key_clone_t)(void *)
Key cloning function type.
Definition: hashmap.h:37
Interface to platform-specific fetcher operations.
Interface to platform-specific miscellaneous browser operation table.
internet structures and defines
Netsurf additional integer type formatting macros.
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:256
Localised message support (interface).
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
const char * nsurl_access(const nsurl *url)
Access a NetSurf URL object as a string.
bool nsurl_has_component(const nsurl *url, nsurl_component part)
Enquire about the existence of componenets in a given URL.
nsurl * nsurl_ref(nsurl *url)
Increment the reference count to a NetSurf URL object.
lwc_string * nsurl_get_component(const nsurl *url, nsurl_component part)
Get part of a URL as a lwc_string, from a NetSurf URL object.
@ NSURL_HOST
Definition: nsurl.h:49
@ NSURL_PORT
Definition: nsurl.h:50
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Ring list structure.
#define RING_GETSIZE(ringtype, ring, sizevar)
Measure the size of a ring and put it in the supplied variable.
Definition: ring.h:86
#define RING_FINDBYLWCHOST(ring, element, lwc_hostname)
Find the element (by hostname) in the given ring, leave it in the provided element variable.
Definition: ring.h:69
#define RING_REMOVE(ring, element)
Remove the given element from the specified ring.
Definition: ring.h:53
#define RING_INSERT(ring, element)
Insert the given item into the specified ring.
Definition: ring.h:40
nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
create new certificate chain
Definition: ssl_certs.c:41
@ SSL_CERT_ERR_TOO_YOUNG
This certificate is not yet valid.
Definition: ssl_certs.h:41
@ SSL_CERT_ERR_BAD_SIG
Bad signature on this certificate.
Definition: ssl_certs.h:40
@ SSL_CERT_ERR_BAD_ISSUER
Bad issuer.
Definition: ssl_certs.h:39
@ SSL_CERT_ERR_SELF_SIGNED
This certificate (or the chain) is self signed.
Definition: ssl_certs.h:43
@ SSL_CERT_ERR_OK
Nothing wrong with this certificate.
Definition: ssl_certs.h:37
@ SSL_CERT_ERR_HOSTNAME_MISMATCH
This certificate host did not match the server.
Definition: ssl_certs.h:46
@ SSL_CERT_ERR_TOO_OLD
This certificate is no longer valid.
Definition: ssl_certs.h:42
@ SSL_CERT_ERR_REVOKED
This certificate has been revoked.
Definition: ssl_certs.h:45
@ SSL_CERT_ERR_CHAIN_SELF_SIGNED
This certificate chain is self signed.
Definition: ssl_certs.h:44
@ SSL_CERT_ERR_CERT_MISSING
This certificate was missing from the chain, its data is useless.
Definition: ssl_certs.h:47
@ SSL_CERT_ERR_UNKNOWN
Unknown error.
Definition: ssl_certs.h:38
nserror cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst)
duplicate a certificate chain into an existing chain
Definition: ssl_certs.c:64
nserror cert_chain_free(struct cert_chain *chain)
free a certificate chain
Definition: ssl_certs.c:317
#define MAX_CERT_DEPTH
maximum number of X509 certificates in chain for TLS connection
Definition: ssl_certs.h:54
Interface to utility string handling.
char * human_friendly_bytesize(unsigned long long int bytesize)
Create a human readable representation of a size in bytes.
Definition: utils.c:209
curl handle cache entry
Definition: curl.c:262
CURL * handle
The cached cURL handle.
Definition: curl.c:263
lwc_string * host
The host for which this handle is cached.
Definition: curl.c:264
struct cache_handle * r_next
Next cached handle in ring.
Definition: curl.c:267
struct cache_handle * r_prev
Previous cached handle in ring.
Definition: curl.c:266
X509 certificate chain.
Definition: ssl_certs.h:59
struct cert_chain::@57 certs[MAX_CERT_DEPTH]
size_t depth
the number of certificates in the chain
Definition: ssl_certs.h:63
uint8_t * der
data in Distinguished Encoding Rules (DER) format
Definition: ssl_certs.h:73
size_t der_length
DER length.
Definition: ssl_certs.h:78
ssl_cert_err err
Whatever is wrong with this certificate.
Definition: ssl_certs.h:68
SSL certificate info.
Definition: curl.c:209
X509 * cert
Pointer to certificate.
Definition: curl.c:210
long err
OpenSSL error code.
Definition: curl.c:211
Information for a single fetch.
Definition: curl.c:235
struct fetch_postdata * postdata
POST data.
Definition: curl.c:251
bool downgrade_tls
Downgrade to TLS 1.2.
Definition: curl.c:243
char * realm
HTTP Auth Realm.
Definition: curl.c:250
bool abort
Abort requested.
Definition: curl.c:240
bool stopped
Download stopped on purpose.
Definition: curl.c:241
bool sent_ssl_chain
Have we tried to send the SSL chain.
Definition: curl.c:238
lwc_string * host
The hostname of this fetch.
Definition: curl.c:245
int cert_depth
deepest certificate in use
Definition: curl.c:257
struct curl_slist * headers
List of request headers.
Definition: curl.c:246
long http_code
HTTP result code from cURL.
Definition: curl.c:254
uint64_t last_progress_update
Time of last progress update.
Definition: curl.c:256
nsurl * url
URL of this fetch.
Definition: curl.c:244
char * cookie_string
Cookie string for this fetch.
Definition: curl.c:249
unsigned long content_length
Response Content-Length, or 0.
Definition: curl.c:248
bool only_2xx
Only HTTP 2xx responses acceptable.
Definition: curl.c:242
CURL * curl_handle
cURL handle if being fetched, or 0.
Definition: curl.c:237
struct fetch * fetch_handle
The fetch handle we're parented by.
Definition: curl.c:236
NSCURL_POSTDATA_T * curl_postdata
POST data in curl representation.
Definition: curl.c:252
char * location
Response Location header, or 0.
Definition: curl.c:247
bool had_headers
Headers have been processed.
Definition: curl.c:239
struct cert_info cert_data[MAX_CERT_DEPTH]
HTTPS certificate data.
Definition: curl.c:258
Fetcher message data.
Definition: fetch.h:72
const uint8_t * buf
Definition: fetch.h:79
fetch_msg_type type
Definition: fetch.h:73
struct fetch_msg::@118::@119 header_or_data
const char * realm
Definition: fetch.h:89
const struct cert_chain * chain
Definition: fetch.h:92
size_t len
Definition: fetch.h:80
struct fetch_msg::@118::@120 auth
union fetch_msg::@118 data
const char * progress
Definition: fetch.h:76
const char * redirect
Definition: fetch.h:86
const char * error
Definition: fetch.h:83
Fetch POST multipart data.
Definition: fetch.h:109
char * rawfile
Raw filename if file is true.
Definition: fetch.h:115
char * value
Item value.
Definition: fetch.h:113
struct fetch_multipart_data * next
Next in linked list.
Definition: fetch.h:110
char * name
Name of item.
Definition: fetch.h:112
bool file
Item is a file.
Definition: fetch.h:116
fetch POST data
Definition: fetch.h:122
struct fetch_multipart_data * multipart
Multipart post data if type is FETCH_POSTDATA_MULTIPART.
Definition: fetch.h:128
union fetch_postdata::@121 data
char * urlenc
Url encoded POST string if type is FETCH_POSTDATA_URLENC.
Definition: fetch.h:126
fetch_postdata_type type
Definition: fetch.h:123
Information for a single fetch.
Definition: fetch.c:89
long http_code
HTTP response code, or 0.
Definition: fetch.c:96
nsurl * url
URL.
Definition: fetch.c:91
lwc_string * host
Host part of URL, interned.
Definition: fetch.c:95
Fetcher operations API.
Definition: fetchers.h:49
bool(* initialise)(lwc_string *scheme)
The initialiser for the fetcher.
Definition: fetchers.h:55
char *(* mimetype)(const char *ro_path)
Find a MIME type for a local file.
Definition: fetch.h:100
int(* socket_close)(int socket)
Close a socket.
Definition: fetch.h:118
int(* socket_open)(int domain, int type, int protocol)
Open a socket.
Definition: fetch.h:110
nserror(* basename)(const char *path, char **str, size_t *size)
Get the basename of a file.
Definition: file.h:84
Parameters for hashmaps.
Definition: hashmap.h:77
hashmap_key_clone_t key_clone
A function which when called will clone a key and give ownership of the returned object to the hashma...
Definition: hashmap.h:82
The content of a hashmap.
Definition: hashmap.c:43
struct gui_file_table * file
File table.
Definition: gui_table.h:104
struct gui_fetch_table * fetch
Fetcher table.
Definition: gui_table.h:94
Interface to time operations.
bool urldb_get_cert_permissions(nsurl *url)
Retrieve certificate verification permissions from database.
Definition: urldb.c:3476
const char * urldb_get_auth_details(nsurl *url, const char *realm)
Look up authentication details in database.
Definition: urldb.c:3401
char * urldb_get_cookie(nsurl *url, bool include_http_only)
Retrieve cookies for an URL.
Definition: urldb.c:3993
Unified URL information database internal interface.
const char * user_agent_string(void)
Retrieve the core user agent for this release.
Definition: useragent.c:79
Default operations table for files.
Option reading and saving interface.
#define nsoption_charp(OPTION)
Get the value of a string option.
Definition: nsoption.h:335
#define nsoption_int(OPTION)
Get the value of an integer option.
Definition: nsoption.h:317
#define nsoption_uint(OPTION)
Get the value of an unsigned integer option.
Definition: nsoption.h:326
@ OPTION_HTTP_PROXY_AUTH_NONE
Definition: nsoption.h:88
@ OPTION_HTTP_PROXY_AUTH_BASIC
Definition: nsoption.h:89
#define nsoption_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:308
Interface to a number of general purpose functionality.