2 * Copyright (C) 2014-2023 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * To get a libcurl handle, call get_handle(). When you hold the
36 * handle, it is yours exclusively to use. After you have finished
37 * with the handle, put it back into the pool by calling put_handle().
51 #include <curl/curl.h>
53 #include <nbdkit-plugin.h>
55 #include "ascii-ctype.h"
56 #include "ascii-string.h"
61 /* Translate CURLcode to nbdkit_error. */
62 #define display_curl_error(ch, r, fs, ...) \
64 nbdkit_error ((fs ": %s: %s"), ## __VA_ARGS__, \
65 curl_easy_strerror ((r)), (ch)->errbuf); \
68 static struct curl_handle
*allocate_handle (void);
69 static int debug_cb (CURL
*handle
, curl_infotype type
,
70 const char *data
, size_t size
, void *);
71 static size_t header_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
);
72 static size_t write_cb (char *ptr
, size_t size
, size_t nmemb
, void *opaque
);
73 static size_t read_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
);
75 /* In the current implementation there is only one handle. This lock
76 * prevents it from being used multiple times.
78 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
80 /* The single curl handle. NULL means not yet allocated. */
81 static struct curl_handle
*the_ch
;
83 /* Close and free all handles in the pool. */
85 free_all_handles (void)
88 curl_easy_cleanup (the_ch
->c
);
89 if (the_ch
->headers_copy
)
90 curl_slist_free_all (the_ch
->headers_copy
);
95 /* Get a handle from the pool.
97 * It is owned exclusively by the caller until they call put_handle.
104 r
= pthread_mutex_lock (&lock
);
107 the_ch
= allocate_handle ();
109 pthread_mutex_unlock (&lock
);
116 /* Return the handle to the pool. */
118 put_handle (struct curl_handle
*ch
)
120 pthread_mutex_unlock (&lock
);
123 /* Allocate and initialize a new libcurl handle. */
124 static struct curl_handle
*
125 allocate_handle (void)
127 struct curl_handle
*ch
;
129 #ifdef HAVE_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
135 ch
= calloc (1, sizeof *ch
);
137 nbdkit_error ("calloc: %m");
142 ch
->c
= curl_easy_init ();
144 nbdkit_error ("curl_easy_init: failed: %m");
148 if (curl_debug_verbose
) {
149 /* NB: Constants must be explicitly long because the parameter is
152 curl_easy_setopt (ch
->c
, CURLOPT_VERBOSE
, 1L);
153 curl_easy_setopt (ch
->c
, CURLOPT_DEBUGFUNCTION
, debug_cb
);
156 curl_easy_setopt (ch
->c
, CURLOPT_ERRORBUFFER
, ch
->errbuf
);
159 if (unix_socket_path
) {
160 #if HAVE_CURLOPT_UNIX_SOCKET_PATH
161 r
= curl_easy_setopt (ch
->c
, CURLOPT_UNIX_SOCKET_PATH
, unix_socket_path
);
163 r
= CURLE_UNKNOWN_OPTION
;
167 display_curl_error (ch
, r
, "curl_easy_setopt: CURLOPT_UNIX_SOCKET_PATH");
172 r
= curl_easy_setopt (ch
->c
, CURLOPT_URL
, url
);
174 display_curl_error (ch
, r
, "curl_easy_setopt: CURLOPT_URL [%s]", url
);
178 /* Various options we always set.
180 * NB: Both here and below constants must be explicitly long because
181 * the parameter is varargs.
183 * For use of CURLOPT_NOSIGNAL see:
184 * https://curl.se/libcurl/c/CURLOPT_NOSIGNAL.html
186 curl_easy_setopt (ch
->c
, CURLOPT_NOSIGNAL
, 1L);
187 curl_easy_setopt (ch
->c
, CURLOPT_AUTOREFERER
, 1L);
189 curl_easy_setopt (ch
->c
, CURLOPT_FOLLOWLOCATION
, 1L);
190 curl_easy_setopt (ch
->c
, CURLOPT_FAILONERROR
, 1L);
194 if (strlen (cainfo
) == 0)
195 curl_easy_setopt (ch
->c
, CURLOPT_CAINFO
, NULL
);
197 curl_easy_setopt (ch
->c
, CURLOPT_CAINFO
, cainfo
);
200 curl_easy_setopt (ch
->c
, CURLOPT_CAPATH
, capath
);
202 curl_easy_setopt (ch
->c
, CURLOPT_COOKIE
, cookie
);
204 curl_easy_setopt (ch
->c
, CURLOPT_COOKIEFILE
, cookiefile
);
206 curl_easy_setopt (ch
->c
, CURLOPT_COOKIEJAR
, cookiejar
);
208 curl_easy_setopt (ch
->c
, CURLOPT_HTTPHEADER
, headers
);
210 curl_easy_setopt (ch
->c
, CURLOPT_PASSWORD
, password
);
211 #ifndef HAVE_CURLOPT_PROTOCOLS_STR
212 if (protocols
!= CURLPROTO_ALL
) {
213 curl_easy_setopt (ch
->c
, CURLOPT_PROTOCOLS
, protocols
);
214 curl_easy_setopt (ch
->c
, CURLOPT_REDIR_PROTOCOLS
, protocols
);
216 #else /* HAVE_CURLOPT_PROTOCOLS_STR (new in 7.85.0) */
218 curl_easy_setopt (ch
->c
, CURLOPT_PROTOCOLS_STR
, protocols
);
219 curl_easy_setopt (ch
->c
, CURLOPT_REDIR_PROTOCOLS_STR
, protocols
);
221 #endif /* HAVE_CURLOPT_PROTOCOLS_STR */
223 curl_easy_setopt (ch
->c
, CURLOPT_PROXY
, proxy
);
225 curl_easy_setopt (ch
->c
, CURLOPT_PROXYPASSWORD
, proxy_password
);
227 curl_easy_setopt (ch
->c
, CURLOPT_PROXYUSERNAME
, proxy_user
);
229 curl_easy_setopt (ch
->c
, CURLOPT_SSL_VERIFYPEER
, 0L);
230 curl_easy_setopt (ch
->c
, CURLOPT_SSL_VERIFYHOST
, 0L);
233 if (strcmp (ssl_version
, "tlsv1") == 0)
234 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1
);
235 else if (strcmp (ssl_version
, "sslv2") == 0)
236 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_SSLv2
);
237 else if (strcmp (ssl_version
, "sslv3") == 0)
238 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_SSLv3
);
239 else if (strcmp (ssl_version
, "tlsv1.0") == 0)
240 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1_0
);
241 else if (strcmp (ssl_version
, "tlsv1.1") == 0)
242 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1_1
);
243 else if (strcmp (ssl_version
, "tlsv1.2") == 0)
244 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1_2
);
245 else if (strcmp (ssl_version
, "tlsv1.3") == 0)
246 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1_3
);
248 display_curl_error (ch
, r
, "curl_easy_setopt: CURLOPT_SSLVERSION [%s]",
255 curl_easy_setopt (ch
->c
, CURLOPT_SSL_CIPHER_LIST
, ssl_cipher_list
);
257 #if (LIBCURL_VERSION_MAJOR > 7) || \
258 (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 61)
259 curl_easy_setopt (ch
->c
, CURLOPT_TLS13_CIPHERS
, tls13_ciphers
);
261 /* This is not available before curl-7.61 */
262 nbdkit_error ("tls13-ciphers is not supported in this build of "
263 "nbdkit-curl-plugin");
268 curl_easy_setopt (ch
->c
, CURLOPT_TCP_KEEPALIVE
, 1L);
270 curl_easy_setopt (ch
->c
, CURLOPT_TCP_NODELAY
, 0L);
272 /* NB: The cast is required here because the parameter is varargs
273 * treated as long, and not type safe.
275 curl_easy_setopt (ch
->c
, CURLOPT_TIMEOUT
, (long) timeout
);
277 curl_easy_setopt (ch
->c
, CURLOPT_USERNAME
, user
);
279 curl_easy_setopt (ch
->c
, CURLOPT_USERAGENT
, user_agent
);
281 /* Get the file size and also whether the remote HTTP server
282 * supports byte ranges.
284 * We must run the scripts if necessary and set headers in the
287 if (do_scripts (ch
) == -1) goto err
;
288 ch
->accept_range
= false;
289 curl_easy_setopt (ch
->c
, CURLOPT_NOBODY
, 1L); /* No Body, not nobody! */
290 curl_easy_setopt (ch
->c
, CURLOPT_HEADERFUNCTION
, header_cb
);
291 curl_easy_setopt (ch
->c
, CURLOPT_HEADERDATA
, ch
);
292 r
= curl_easy_perform (ch
->c
);
294 display_curl_error (ch
, r
,
295 "problem doing HEAD request to fetch size of URL [%s]",
300 #ifdef HAVE_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
301 r
= curl_easy_getinfo (ch
->c
, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
, &o
);
303 display_curl_error (ch
, r
,
304 "could not get length of remote file [%s]", url
);
309 nbdkit_error ("could not get length of remote file [%s], "
310 "is the URL correct?", url
);
316 r
= curl_easy_getinfo (ch
->c
, CURLINFO_CONTENT_LENGTH_DOWNLOAD
, &d
);
318 display_curl_error (ch
, r
,
319 "could not get length of remote file [%s]", url
);
324 nbdkit_error ("could not get length of remote file [%s], "
325 "is the URL correct?", url
);
331 nbdkit_debug ("content length: %" PRIi64
, ch
->exportsize
);
333 if (ascii_strncasecmp (url
, "http://", strlen ("http://")) == 0 ||
334 ascii_strncasecmp (url
, "https://", strlen ("https://")) == 0) {
335 if (!ch
->accept_range
) {
336 nbdkit_error ("server does not support 'range' (byte range) requests");
340 nbdkit_debug ("accept range supported (for HTTP/HTTPS)");
343 /* Get set up for reading and writing. */
344 curl_easy_setopt (ch
->c
, CURLOPT_HEADERFUNCTION
, NULL
);
345 curl_easy_setopt (ch
->c
, CURLOPT_HEADERDATA
, NULL
);
346 curl_easy_setopt (ch
->c
, CURLOPT_WRITEFUNCTION
, write_cb
);
347 curl_easy_setopt (ch
->c
, CURLOPT_WRITEDATA
, ch
);
348 /* These are only used if !readonly but we always register them. */
349 curl_easy_setopt (ch
->c
, CURLOPT_READFUNCTION
, read_cb
);
350 curl_easy_setopt (ch
->c
, CURLOPT_READDATA
, ch
);
356 curl_easy_cleanup (ch
->c
);
361 /* When using CURLOPT_VERBOSE, this callback is used to redirect
362 * messages to nbdkit_debug (instead of stderr).
365 debug_cb (CURL
*handle
, curl_infotype type
,
366 const char *data
, size_t size
, void *opaque
)
368 size_t origsize
= size
;
369 CLEANUP_FREE
char *str
;
371 /* The data parameter passed is NOT \0-terminated, but also it may
372 * have \n or \r\n line endings. The only sane way to deal with
373 * this is to copy the string. (The data strings may also be
374 * multi-line, but we don't deal with that here).
376 str
= malloc (size
+ 1);
379 memcpy (str
, data
, size
);
382 while (size
> 0 && (str
[size
-1] == '\n' || str
[size
-1] == '\r')) {
389 nbdkit_debug ("%s", str
);
391 case CURLINFO_HEADER_IN
:
392 nbdkit_debug ("S: %s", str
);
394 case CURLINFO_HEADER_OUT
:
395 nbdkit_debug ("C: %s", str
);
398 /* Assume everything else is binary data that we cannot print. */
399 nbdkit_debug ("<data with size=%zu>", origsize
);
407 header_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
409 struct curl_handle
*ch
= opaque
;
410 size_t realsize
= size
* nmemb
;
411 const char *header
= ptr
;
412 const char *end
= header
+ realsize
;
413 const char *accept_ranges
= "accept-ranges:";
414 const char *bytes
= "bytes";
416 if (realsize
>= strlen (accept_ranges
) &&
417 ascii_strncasecmp (header
, accept_ranges
, strlen (accept_ranges
)) == 0) {
418 const char *p
= strchr (header
, ':') + 1;
420 /* Skip whitespace between the header name and value. */
421 while (p
< end
&& *p
&& ascii_isspace (*p
))
424 if (end
- p
>= strlen (bytes
)
425 && strncmp (p
, bytes
, strlen (bytes
)) == 0) {
426 /* Check that there is nothing but whitespace after the value. */
428 while (p
< end
&& *p
&& ascii_isspace (*p
))
432 ch
->accept_range
= true;
439 /* NB: The terminology used by libcurl is confusing!
441 * WRITEFUNCTION / write_cb is used when reading from the remote server
442 * READFUNCTION / read_cb is used when writing to the remote server.
444 * We use the same terminology as libcurl here.
448 write_cb (char *ptr
, size_t size
, size_t nmemb
, void *opaque
)
450 struct curl_handle
*ch
= opaque
;
451 size_t orig_realsize
= size
* nmemb
;
452 size_t realsize
= orig_realsize
;
454 assert (ch
->write_buf
);
456 /* Don't read more than the requested amount of data, even if the
457 * server or libcurl sends more.
459 if (realsize
> ch
->write_count
)
460 realsize
= ch
->write_count
;
462 memcpy (ch
->write_buf
, ptr
, realsize
);
464 ch
->write_count
-= realsize
;
465 ch
->write_buf
+= realsize
;
467 return orig_realsize
;
471 read_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
473 struct curl_handle
*ch
= opaque
;
474 size_t realsize
= size
* nmemb
;
476 assert (ch
->read_buf
);
477 if (realsize
> ch
->read_count
)
478 realsize
= ch
->read_count
;
480 memcpy (ptr
, ch
->read_buf
, realsize
);
482 ch
->read_count
-= realsize
;
483 ch
->read_buf
+= realsize
;