1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | * SPDX-License-Identifier: curl |
22 | * |
23 | ***************************************************************************/ |
24 | |
25 | #include "curl_setup.h" |
26 | |
27 | /* |
28 | * See comment in curl_memory.h for the explanation of this sanity check. |
29 | */ |
30 | |
31 | #ifdef CURLX_NO_MEMORY_CALLBACKS |
32 | #error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" |
33 | #endif |
34 | |
35 | #ifdef HAVE_NETINET_IN_H |
36 | #include <netinet/in.h> |
37 | #endif |
38 | #ifdef HAVE_NETDB_H |
39 | #include <netdb.h> |
40 | #endif |
41 | #ifdef HAVE_ARPA_INET_H |
42 | #include <arpa/inet.h> |
43 | #endif |
44 | #ifdef HAVE_NET_IF_H |
45 | #include <net/if.h> |
46 | #endif |
47 | #ifdef HAVE_SYS_IOCTL_H |
48 | #include <sys/ioctl.h> |
49 | #endif |
50 | |
51 | #ifdef HAVE_SYS_PARAM_H |
52 | #include <sys/param.h> |
53 | #endif |
54 | |
55 | #include "urldata.h" |
56 | #include <curl/curl.h> |
57 | #include "transfer.h" |
58 | #include "vtls/vtls.h" |
59 | #include "url.h" |
60 | #include "getinfo.h" |
61 | #include "hostip.h" |
62 | #include "share.h" |
63 | #include "strdup.h" |
64 | #include "progress.h" |
65 | #include "easyif.h" |
66 | #include "multiif.h" |
67 | #include "select.h" |
68 | #include "sendf.h" /* for failf function prototype */ |
69 | #include "connect.h" /* for Curl_getconnectinfo */ |
70 | #include "slist.h" |
71 | #include "mime.h" |
72 | #include "amigaos.h" |
73 | #include "warnless.h" |
74 | #include "multiif.h" |
75 | #include "sigpipe.h" |
76 | #include "vssh/ssh.h" |
77 | #include "setopt.h" |
78 | #include "http_digest.h" |
79 | #include "system_win32.h" |
80 | #include "http2.h" |
81 | #include "dynbuf.h" |
82 | #include "altsvc.h" |
83 | #include "hsts.h" |
84 | |
85 | /* The last 3 #include files should be in this order */ |
86 | #include "curl_printf.h" |
87 | #include "curl_memory.h" |
88 | #include "memdebug.h" |
89 | #include "easy_lock.h" |
90 | |
91 | /* true globals -- for curl_global_init() and curl_global_cleanup() */ |
92 | static unsigned int initialized; |
93 | static long init_flags; |
94 | |
95 | #ifdef GLOBAL_INIT_IS_THREADSAFE |
96 | |
97 | static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; |
98 | #define global_init_lock() curl_simple_lock_lock(&s_lock) |
99 | #define global_init_unlock() curl_simple_lock_unlock(&s_lock) |
100 | |
101 | #else |
102 | |
103 | #define global_init_lock() |
104 | #define global_init_unlock() |
105 | |
106 | #endif |
107 | |
108 | /* |
109 | * strdup (and other memory functions) is redefined in complicated |
110 | * ways, but at this point it must be defined as the system-supplied strdup |
111 | * so the callback pointer is initialized correctly. |
112 | */ |
113 | #if defined(_WIN32_WCE) |
114 | #define system_strdup _strdup |
115 | #elif !defined(HAVE_STRDUP) |
116 | #define system_strdup curlx_strdup |
117 | #else |
118 | #define system_strdup strdup |
119 | #endif |
120 | |
121 | #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) |
122 | # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ |
123 | #endif |
124 | |
125 | /* |
126 | * If a memory-using function (like curl_getenv) is used before |
127 | * curl_global_init() is called, we need to have these pointers set already. |
128 | */ |
129 | curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; |
130 | curl_free_callback Curl_cfree = (curl_free_callback)free; |
131 | curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; |
132 | curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; |
133 | curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; |
134 | #if defined(WIN32) && defined(UNICODE) |
135 | curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; |
136 | #endif |
137 | |
138 | #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) |
139 | # pragma warning(default:4232) /* MSVC extension, dllimport identity */ |
140 | #endif |
141 | |
142 | #ifdef DEBUGBUILD |
143 | static char *leakpointer; |
144 | #endif |
145 | |
146 | /** |
147 | * curl_global_init() globally initializes curl given a bitwise set of the |
148 | * different features of what to initialize. |
149 | */ |
150 | static CURLcode global_init(long flags, bool memoryfuncs) |
151 | { |
152 | if(initialized++) |
153 | return CURLE_OK; |
154 | |
155 | if(memoryfuncs) { |
156 | /* Setup the default memory functions here (again) */ |
157 | Curl_cmalloc = (curl_malloc_callback)malloc; |
158 | Curl_cfree = (curl_free_callback)free; |
159 | Curl_crealloc = (curl_realloc_callback)realloc; |
160 | Curl_cstrdup = (curl_strdup_callback)system_strdup; |
161 | Curl_ccalloc = (curl_calloc_callback)calloc; |
162 | #if defined(WIN32) && defined(UNICODE) |
163 | Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; |
164 | #endif |
165 | } |
166 | |
167 | if(!Curl_ssl_init()) { |
168 | DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n" )); |
169 | goto fail; |
170 | } |
171 | |
172 | #ifdef WIN32 |
173 | if(Curl_win32_init(flags)) { |
174 | DEBUGF(fprintf(stderr, "Error: win32_init failed\n" )); |
175 | goto fail; |
176 | } |
177 | #endif |
178 | |
179 | #ifdef __AMIGA__ |
180 | if(Curl_amiga_init()) { |
181 | DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n" )); |
182 | goto fail; |
183 | } |
184 | #endif |
185 | |
186 | if(Curl_resolver_global_init()) { |
187 | DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n" )); |
188 | goto fail; |
189 | } |
190 | |
191 | #if defined(USE_SSH) |
192 | if(Curl_ssh_init()) { |
193 | goto fail; |
194 | } |
195 | #endif |
196 | |
197 | #ifdef USE_WOLFSSH |
198 | if(WS_SUCCESS != wolfSSH_Init()) { |
199 | DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n" )); |
200 | return CURLE_FAILED_INIT; |
201 | } |
202 | #endif |
203 | |
204 | init_flags = flags; |
205 | |
206 | #ifdef DEBUGBUILD |
207 | if(getenv("CURL_GLOBAL_INIT" )) |
208 | /* alloc data that will leak if *cleanup() is not called! */ |
209 | leakpointer = malloc(1); |
210 | #endif |
211 | |
212 | return CURLE_OK; |
213 | |
214 | fail: |
215 | initialized--; /* undo the increase */ |
216 | return CURLE_FAILED_INIT; |
217 | } |
218 | |
219 | |
220 | /** |
221 | * curl_global_init() globally initializes curl given a bitwise set of the |
222 | * different features of what to initialize. |
223 | */ |
224 | CURLcode curl_global_init(long flags) |
225 | { |
226 | CURLcode result; |
227 | global_init_lock(); |
228 | |
229 | result = global_init(flags, TRUE); |
230 | |
231 | global_init_unlock(); |
232 | |
233 | return result; |
234 | } |
235 | |
236 | /* |
237 | * curl_global_init_mem() globally initializes curl and also registers the |
238 | * user provided callback routines. |
239 | */ |
240 | CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, |
241 | curl_free_callback f, curl_realloc_callback r, |
242 | curl_strdup_callback s, curl_calloc_callback c) |
243 | { |
244 | CURLcode result; |
245 | |
246 | /* Invalid input, return immediately */ |
247 | if(!m || !f || !r || !s || !c) |
248 | return CURLE_FAILED_INIT; |
249 | |
250 | global_init_lock(); |
251 | |
252 | if(initialized) { |
253 | /* Already initialized, don't do it again, but bump the variable anyway to |
254 | work like curl_global_init() and require the same amount of cleanup |
255 | calls. */ |
256 | initialized++; |
257 | global_init_unlock(); |
258 | return CURLE_OK; |
259 | } |
260 | |
261 | /* set memory functions before global_init() in case it wants memory |
262 | functions */ |
263 | Curl_cmalloc = m; |
264 | Curl_cfree = f; |
265 | Curl_cstrdup = s; |
266 | Curl_crealloc = r; |
267 | Curl_ccalloc = c; |
268 | |
269 | /* Call the actual init function, but without setting */ |
270 | result = global_init(flags, FALSE); |
271 | |
272 | global_init_unlock(); |
273 | |
274 | return result; |
275 | } |
276 | |
277 | /** |
278 | * curl_global_cleanup() globally cleanups curl, uses the value of |
279 | * "init_flags" to determine what needs to be cleaned up and what doesn't. |
280 | */ |
281 | void curl_global_cleanup(void) |
282 | { |
283 | global_init_lock(); |
284 | |
285 | if(!initialized) { |
286 | global_init_unlock(); |
287 | return; |
288 | } |
289 | |
290 | if(--initialized) { |
291 | global_init_unlock(); |
292 | return; |
293 | } |
294 | |
295 | Curl_ssl_cleanup(); |
296 | Curl_resolver_global_cleanup(); |
297 | |
298 | #ifdef WIN32 |
299 | Curl_win32_cleanup(init_flags); |
300 | #endif |
301 | |
302 | Curl_amiga_cleanup(); |
303 | |
304 | Curl_ssh_cleanup(); |
305 | |
306 | #ifdef USE_WOLFSSH |
307 | (void)wolfSSH_Cleanup(); |
308 | #endif |
309 | #ifdef DEBUGBUILD |
310 | free(leakpointer); |
311 | #endif |
312 | |
313 | init_flags = 0; |
314 | |
315 | global_init_unlock(); |
316 | } |
317 | |
318 | /* |
319 | * curl_global_sslset() globally initializes the SSL backend to use. |
320 | */ |
321 | CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, |
322 | const curl_ssl_backend ***avail) |
323 | { |
324 | CURLsslset rc; |
325 | |
326 | global_init_lock(); |
327 | |
328 | rc = Curl_init_sslset_nolock(id, name, avail); |
329 | |
330 | global_init_unlock(); |
331 | |
332 | return rc; |
333 | } |
334 | |
335 | /* |
336 | * curl_easy_init() is the external interface to alloc, setup and init an |
337 | * easy handle that is returned. If anything goes wrong, NULL is returned. |
338 | */ |
339 | struct Curl_easy *curl_easy_init(void) |
340 | { |
341 | CURLcode result; |
342 | struct Curl_easy *data; |
343 | |
344 | /* Make sure we inited the global SSL stuff */ |
345 | global_init_lock(); |
346 | |
347 | if(!initialized) { |
348 | result = global_init(CURL_GLOBAL_DEFAULT, TRUE); |
349 | if(result) { |
350 | /* something in the global init failed, return nothing */ |
351 | DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n" )); |
352 | global_init_unlock(); |
353 | return NULL; |
354 | } |
355 | } |
356 | global_init_unlock(); |
357 | |
358 | /* We use curl_open() with undefined URL so far */ |
359 | result = Curl_open(&data); |
360 | if(result) { |
361 | DEBUGF(fprintf(stderr, "Error: Curl_open failed\n" )); |
362 | return NULL; |
363 | } |
364 | |
365 | return data; |
366 | } |
367 | |
368 | #ifdef CURLDEBUG |
369 | |
370 | struct socketmonitor { |
371 | struct socketmonitor *next; /* the next node in the list or NULL */ |
372 | struct pollfd socket; /* socket info of what to monitor */ |
373 | }; |
374 | |
375 | struct events { |
376 | long ms; /* timeout, run the timeout function when reached */ |
377 | bool msbump; /* set TRUE when timeout is set by callback */ |
378 | int num_sockets; /* number of nodes in the monitor list */ |
379 | struct socketmonitor *list; /* list of sockets to monitor */ |
380 | int running_handles; /* store the returned number */ |
381 | }; |
382 | |
383 | /* events_timer |
384 | * |
385 | * Callback that gets called with a new value when the timeout should be |
386 | * updated. |
387 | */ |
388 | |
389 | static int events_timer(struct Curl_multi *multi, /* multi handle */ |
390 | long timeout_ms, /* see above */ |
391 | void *userp) /* private callback pointer */ |
392 | { |
393 | struct events *ev = userp; |
394 | (void)multi; |
395 | if(timeout_ms == -1) |
396 | /* timeout removed */ |
397 | timeout_ms = 0; |
398 | else if(timeout_ms == 0) |
399 | /* timeout is already reached! */ |
400 | timeout_ms = 1; /* trigger asap */ |
401 | |
402 | ev->ms = timeout_ms; |
403 | ev->msbump = TRUE; |
404 | return 0; |
405 | } |
406 | |
407 | |
408 | /* poll2cselect |
409 | * |
410 | * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones |
411 | */ |
412 | static int poll2cselect(int pollmask) |
413 | { |
414 | int omask = 0; |
415 | if(pollmask & POLLIN) |
416 | omask |= CURL_CSELECT_IN; |
417 | if(pollmask & POLLOUT) |
418 | omask |= CURL_CSELECT_OUT; |
419 | if(pollmask & POLLERR) |
420 | omask |= CURL_CSELECT_ERR; |
421 | return omask; |
422 | } |
423 | |
424 | |
425 | /* socketcb2poll |
426 | * |
427 | * convert from libcurl' CURL_POLL_* bit definitions to poll()'s |
428 | */ |
429 | static short socketcb2poll(int pollmask) |
430 | { |
431 | short omask = 0; |
432 | if(pollmask & CURL_POLL_IN) |
433 | omask |= POLLIN; |
434 | if(pollmask & CURL_POLL_OUT) |
435 | omask |= POLLOUT; |
436 | return omask; |
437 | } |
438 | |
439 | /* events_socket |
440 | * |
441 | * Callback that gets called with information about socket activity to |
442 | * monitor. |
443 | */ |
444 | static int events_socket(struct Curl_easy *easy, /* easy handle */ |
445 | curl_socket_t s, /* socket */ |
446 | int what, /* see above */ |
447 | void *userp, /* private callback |
448 | pointer */ |
449 | void *socketp) /* private socket |
450 | pointer */ |
451 | { |
452 | struct events *ev = userp; |
453 | struct socketmonitor *m; |
454 | struct socketmonitor *prev = NULL; |
455 | |
456 | #if defined(CURL_DISABLE_VERBOSE_STRINGS) |
457 | (void) easy; |
458 | #endif |
459 | (void)socketp; |
460 | |
461 | m = ev->list; |
462 | while(m) { |
463 | if(m->socket.fd == s) { |
464 | |
465 | if(what == CURL_POLL_REMOVE) { |
466 | struct socketmonitor *nxt = m->next; |
467 | /* remove this node from the list of monitored sockets */ |
468 | if(prev) |
469 | prev->next = nxt; |
470 | else |
471 | ev->list = nxt; |
472 | free(m); |
473 | m = nxt; |
474 | infof(easy, "socket cb: socket %d REMOVED" , s); |
475 | } |
476 | else { |
477 | /* The socket 's' is already being monitored, update the activity |
478 | mask. Convert from libcurl bitmask to the poll one. */ |
479 | m->socket.events = socketcb2poll(what); |
480 | infof(easy, "socket cb: socket %d UPDATED as %s%s" , s, |
481 | (what&CURL_POLL_IN)?"IN" :"" , |
482 | (what&CURL_POLL_OUT)?"OUT" :"" ); |
483 | } |
484 | break; |
485 | } |
486 | prev = m; |
487 | m = m->next; /* move to next node */ |
488 | } |
489 | if(!m) { |
490 | if(what == CURL_POLL_REMOVE) { |
491 | /* this happens a bit too often, libcurl fix perhaps? */ |
492 | /* fprintf(stderr, |
493 | "%s: socket %d asked to be REMOVED but not present!\n", |
494 | __func__, s); */ |
495 | } |
496 | else { |
497 | m = malloc(sizeof(struct socketmonitor)); |
498 | if(m) { |
499 | m->next = ev->list; |
500 | m->socket.fd = s; |
501 | m->socket.events = socketcb2poll(what); |
502 | m->socket.revents = 0; |
503 | ev->list = m; |
504 | infof(easy, "socket cb: socket %d ADDED as %s%s" , s, |
505 | (what&CURL_POLL_IN)?"IN" :"" , |
506 | (what&CURL_POLL_OUT)?"OUT" :"" ); |
507 | } |
508 | else |
509 | return CURLE_OUT_OF_MEMORY; |
510 | } |
511 | } |
512 | |
513 | return 0; |
514 | } |
515 | |
516 | |
517 | /* |
518 | * events_setup() |
519 | * |
520 | * Do the multi handle setups that only event-based transfers need. |
521 | */ |
522 | static void events_setup(struct Curl_multi *multi, struct events *ev) |
523 | { |
524 | /* timer callback */ |
525 | curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); |
526 | curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); |
527 | |
528 | /* socket callback */ |
529 | curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); |
530 | curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); |
531 | } |
532 | |
533 | |
534 | /* wait_or_timeout() |
535 | * |
536 | * waits for activity on any of the given sockets, or the timeout to trigger. |
537 | */ |
538 | |
539 | static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) |
540 | { |
541 | bool done = FALSE; |
542 | CURLMcode mcode = CURLM_OK; |
543 | CURLcode result = CURLE_OK; |
544 | |
545 | while(!done) { |
546 | CURLMsg *msg; |
547 | struct socketmonitor *m; |
548 | struct pollfd *f; |
549 | struct pollfd fds[4]; |
550 | int numfds = 0; |
551 | int pollrc; |
552 | int i; |
553 | struct curltime before; |
554 | struct curltime after; |
555 | |
556 | /* populate the fds[] array */ |
557 | for(m = ev->list, f = &fds[0]; m; m = m->next) { |
558 | f->fd = m->socket.fd; |
559 | f->events = m->socket.events; |
560 | f->revents = 0; |
561 | /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ |
562 | f++; |
563 | numfds++; |
564 | } |
565 | |
566 | /* get the time stamp to use to figure out how long poll takes */ |
567 | before = Curl_now(); |
568 | |
569 | /* wait for activity or timeout */ |
570 | pollrc = Curl_poll(fds, numfds, ev->ms); |
571 | if(pollrc < 0) |
572 | return CURLE_UNRECOVERABLE_POLL; |
573 | |
574 | after = Curl_now(); |
575 | |
576 | ev->msbump = FALSE; /* reset here */ |
577 | |
578 | if(!pollrc) { |
579 | /* timeout! */ |
580 | ev->ms = 0; |
581 | /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ |
582 | mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, |
583 | &ev->running_handles); |
584 | } |
585 | else { |
586 | /* here pollrc is > 0 */ |
587 | |
588 | /* loop over the monitored sockets to see which ones had activity */ |
589 | for(i = 0; i< numfds; i++) { |
590 | if(fds[i].revents) { |
591 | /* socket activity, tell libcurl */ |
592 | int act = poll2cselect(fds[i].revents); /* convert */ |
593 | infof(multi->easyp, "call curl_multi_socket_action(socket %d)" , |
594 | fds[i].fd); |
595 | mcode = curl_multi_socket_action(multi, fds[i].fd, act, |
596 | &ev->running_handles); |
597 | } |
598 | } |
599 | |
600 | if(!ev->msbump) { |
601 | /* If nothing updated the timeout, we decrease it by the spent time. |
602 | * If it was updated, it has the new timeout time stored already. |
603 | */ |
604 | timediff_t timediff = Curl_timediff(after, before); |
605 | if(timediff > 0) { |
606 | if(timediff > ev->ms) |
607 | ev->ms = 0; |
608 | else |
609 | ev->ms -= (long)timediff; |
610 | } |
611 | } |
612 | } |
613 | |
614 | if(mcode) |
615 | return CURLE_URL_MALFORMAT; |
616 | |
617 | /* we don't really care about the "msgs_in_queue" value returned in the |
618 | second argument */ |
619 | msg = curl_multi_info_read(multi, &pollrc); |
620 | if(msg) { |
621 | result = msg->data.result; |
622 | done = TRUE; |
623 | } |
624 | } |
625 | |
626 | return result; |
627 | } |
628 | |
629 | |
630 | /* easy_events() |
631 | * |
632 | * Runs a transfer in a blocking manner using the events-based API |
633 | */ |
634 | static CURLcode easy_events(struct Curl_multi *multi) |
635 | { |
636 | /* this struct is made static to allow it to be used after this function |
637 | returns and curl_multi_remove_handle() is called */ |
638 | static struct events evs = {2, FALSE, 0, NULL, 0}; |
639 | |
640 | /* if running event-based, do some further multi inits */ |
641 | events_setup(multi, &evs); |
642 | |
643 | return wait_or_timeout(multi, &evs); |
644 | } |
645 | #else /* CURLDEBUG */ |
646 | /* when not built with debug, this function doesn't exist */ |
647 | #define easy_events(x) CURLE_NOT_BUILT_IN |
648 | #endif |
649 | |
650 | static CURLcode easy_transfer(struct Curl_multi *multi) |
651 | { |
652 | bool done = FALSE; |
653 | CURLMcode mcode = CURLM_OK; |
654 | CURLcode result = CURLE_OK; |
655 | |
656 | while(!done && !mcode) { |
657 | int still_running = 0; |
658 | |
659 | mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); |
660 | |
661 | if(!mcode) |
662 | mcode = curl_multi_perform(multi, &still_running); |
663 | |
664 | /* only read 'still_running' if curl_multi_perform() return OK */ |
665 | if(!mcode && !still_running) { |
666 | int rc; |
667 | CURLMsg *msg = curl_multi_info_read(multi, &rc); |
668 | if(msg) { |
669 | result = msg->data.result; |
670 | done = TRUE; |
671 | } |
672 | } |
673 | } |
674 | |
675 | /* Make sure to return some kind of error if there was a multi problem */ |
676 | if(mcode) { |
677 | result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : |
678 | /* The other multi errors should never happen, so return |
679 | something suitably generic */ |
680 | CURLE_BAD_FUNCTION_ARGUMENT; |
681 | } |
682 | |
683 | return result; |
684 | } |
685 | |
686 | |
687 | /* |
688 | * easy_perform() is the external interface that performs a blocking |
689 | * transfer as previously setup. |
690 | * |
691 | * CONCEPT: This function creates a multi handle, adds the easy handle to it, |
692 | * runs curl_multi_perform() until the transfer is done, then detaches the |
693 | * easy handle, destroys the multi handle and returns the easy handle's return |
694 | * code. |
695 | * |
696 | * REALITY: it can't just create and destroy the multi handle that easily. It |
697 | * needs to keep it around since if this easy handle is used again by this |
698 | * function, the same multi handle must be re-used so that the same pools and |
699 | * caches can be used. |
700 | * |
701 | * DEBUG: if 'events' is set TRUE, this function will use a replacement engine |
702 | * instead of curl_multi_perform() and use curl_multi_socket_action(). |
703 | */ |
704 | static CURLcode easy_perform(struct Curl_easy *data, bool events) |
705 | { |
706 | struct Curl_multi *multi; |
707 | CURLMcode mcode; |
708 | CURLcode result = CURLE_OK; |
709 | SIGPIPE_VARIABLE(pipe_st); |
710 | |
711 | if(!data) |
712 | return CURLE_BAD_FUNCTION_ARGUMENT; |
713 | |
714 | if(data->set.errorbuffer) |
715 | /* clear this as early as possible */ |
716 | data->set.errorbuffer[0] = 0; |
717 | |
718 | if(data->multi) { |
719 | failf(data, "easy handle already used in multi handle" ); |
720 | return CURLE_FAILED_INIT; |
721 | } |
722 | |
723 | if(data->multi_easy) |
724 | multi = data->multi_easy; |
725 | else { |
726 | /* this multi handle will only ever have a single easy handled attached |
727 | to it, so make it use minimal hashes */ |
728 | multi = Curl_multi_handle(1, 3, 7); |
729 | if(!multi) |
730 | return CURLE_OUT_OF_MEMORY; |
731 | data->multi_easy = multi; |
732 | } |
733 | |
734 | if(multi->in_callback) |
735 | return CURLE_RECURSIVE_API_CALL; |
736 | |
737 | /* Copy the MAXCONNECTS option to the multi handle */ |
738 | curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); |
739 | |
740 | mcode = curl_multi_add_handle(multi, data); |
741 | if(mcode) { |
742 | curl_multi_cleanup(multi); |
743 | data->multi_easy = NULL; |
744 | if(mcode == CURLM_OUT_OF_MEMORY) |
745 | return CURLE_OUT_OF_MEMORY; |
746 | return CURLE_FAILED_INIT; |
747 | } |
748 | |
749 | sigpipe_ignore(data, &pipe_st); |
750 | |
751 | /* run the transfer */ |
752 | result = events ? easy_events(multi) : easy_transfer(multi); |
753 | |
754 | /* ignoring the return code isn't nice, but atm we can't really handle |
755 | a failure here, room for future improvement! */ |
756 | (void)curl_multi_remove_handle(multi, data); |
757 | |
758 | sigpipe_restore(&pipe_st); |
759 | |
760 | /* The multi handle is kept alive, owned by the easy handle */ |
761 | return result; |
762 | } |
763 | |
764 | |
765 | /* |
766 | * curl_easy_perform() is the external interface that performs a blocking |
767 | * transfer as previously setup. |
768 | */ |
769 | CURLcode curl_easy_perform(struct Curl_easy *data) |
770 | { |
771 | return easy_perform(data, FALSE); |
772 | } |
773 | |
774 | #ifdef CURLDEBUG |
775 | /* |
776 | * curl_easy_perform_ev() is the external interface that performs a blocking |
777 | * transfer using the event-based API internally. |
778 | */ |
779 | CURLcode curl_easy_perform_ev(struct Curl_easy *data) |
780 | { |
781 | return easy_perform(data, TRUE); |
782 | } |
783 | |
784 | #endif |
785 | |
786 | /* |
787 | * curl_easy_cleanup() is the external interface to cleaning/freeing the given |
788 | * easy handle. |
789 | */ |
790 | void curl_easy_cleanup(struct Curl_easy *data) |
791 | { |
792 | SIGPIPE_VARIABLE(pipe_st); |
793 | |
794 | if(!data) |
795 | return; |
796 | |
797 | sigpipe_ignore(data, &pipe_st); |
798 | Curl_close(&data); |
799 | sigpipe_restore(&pipe_st); |
800 | } |
801 | |
802 | /* |
803 | * curl_easy_getinfo() is an external interface that allows an app to retrieve |
804 | * information from a performed transfer and similar. |
805 | */ |
806 | #undef curl_easy_getinfo |
807 | CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) |
808 | { |
809 | va_list arg; |
810 | void *paramp; |
811 | CURLcode result; |
812 | |
813 | va_start(arg, info); |
814 | paramp = va_arg(arg, void *); |
815 | |
816 | result = Curl_getinfo(data, info, paramp); |
817 | |
818 | va_end(arg); |
819 | return result; |
820 | } |
821 | |
822 | static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) |
823 | { |
824 | CURLcode result = CURLE_OK; |
825 | enum dupstring i; |
826 | enum dupblob j; |
827 | |
828 | /* Copy src->set into dst->set first, then deal with the strings |
829 | afterwards */ |
830 | dst->set = src->set; |
831 | Curl_mime_initpart(&dst->set.mimepost, dst); |
832 | |
833 | /* clear all string pointers first */ |
834 | memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); |
835 | |
836 | /* duplicate all strings */ |
837 | for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { |
838 | result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); |
839 | if(result) |
840 | return result; |
841 | } |
842 | |
843 | /* clear all blob pointers first */ |
844 | memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); |
845 | /* duplicate all blobs */ |
846 | for(j = (enum dupblob)0; j < BLOB_LAST; j++) { |
847 | result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); |
848 | if(result) |
849 | return result; |
850 | } |
851 | |
852 | /* duplicate memory areas pointed to */ |
853 | i = STRING_COPYPOSTFIELDS; |
854 | if(src->set.postfieldsize && src->set.str[i]) { |
855 | /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ |
856 | dst->set.str[i] = Curl_memdup(src->set.str[i], |
857 | curlx_sotouz(src->set.postfieldsize)); |
858 | if(!dst->set.str[i]) |
859 | return CURLE_OUT_OF_MEMORY; |
860 | /* point to the new copy */ |
861 | dst->set.postfields = dst->set.str[i]; |
862 | } |
863 | |
864 | /* Duplicate mime data. */ |
865 | result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); |
866 | |
867 | if(src->set.resolve) |
868 | dst->state.resolve = dst->set.resolve; |
869 | |
870 | return result; |
871 | } |
872 | |
873 | /* |
874 | * curl_easy_duphandle() is an external interface to allow duplication of a |
875 | * given input easy handle. The returned handle will be a new working handle |
876 | * with all options set exactly as the input source handle. |
877 | */ |
878 | struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) |
879 | { |
880 | struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); |
881 | if(!outcurl) |
882 | goto fail; |
883 | |
884 | /* |
885 | * We setup a few buffers we need. We should probably make them |
886 | * get setup on-demand in the code, as that would probably decrease |
887 | * the likeliness of us forgetting to init a buffer here in the future. |
888 | */ |
889 | outcurl->set.buffer_size = data->set.buffer_size; |
890 | |
891 | /* copy all userdefined values */ |
892 | if(dupset(outcurl, data)) |
893 | goto fail; |
894 | |
895 | Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); |
896 | |
897 | /* the connection cache is setup on demand */ |
898 | outcurl->state.conn_cache = NULL; |
899 | outcurl->state.lastconnect_id = -1; |
900 | |
901 | outcurl->progress.flags = data->progress.flags; |
902 | outcurl->progress.callback = data->progress.callback; |
903 | |
904 | #ifndef CURL_DISABLE_COOKIES |
905 | if(data->cookies) { |
906 | /* If cookies are enabled in the parent handle, we enable them |
907 | in the clone as well! */ |
908 | outcurl->cookies = Curl_cookie_init(data, |
909 | data->cookies->filename, |
910 | outcurl->cookies, |
911 | data->set.cookiesession); |
912 | if(!outcurl->cookies) |
913 | goto fail; |
914 | } |
915 | |
916 | /* duplicate all values in 'change' */ |
917 | if(data->state.cookielist) { |
918 | outcurl->state.cookielist = |
919 | Curl_slist_duplicate(data->state.cookielist); |
920 | if(!outcurl->state.cookielist) |
921 | goto fail; |
922 | } |
923 | #endif |
924 | |
925 | if(data->state.url) { |
926 | outcurl->state.url = strdup(data->state.url); |
927 | if(!outcurl->state.url) |
928 | goto fail; |
929 | outcurl->state.url_alloc = TRUE; |
930 | } |
931 | |
932 | if(data->state.referer) { |
933 | outcurl->state.referer = strdup(data->state.referer); |
934 | if(!outcurl->state.referer) |
935 | goto fail; |
936 | outcurl->state.referer_alloc = TRUE; |
937 | } |
938 | |
939 | /* Reinitialize an SSL engine for the new handle |
940 | * note: the engine name has already been copied by dupset */ |
941 | if(outcurl->set.str[STRING_SSL_ENGINE]) { |
942 | if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) |
943 | goto fail; |
944 | } |
945 | |
946 | #ifdef USE_ALTSVC |
947 | if(data->asi) { |
948 | outcurl->asi = Curl_altsvc_init(); |
949 | if(!outcurl->asi) |
950 | goto fail; |
951 | if(outcurl->set.str[STRING_ALTSVC]) |
952 | (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); |
953 | } |
954 | #endif |
955 | #ifndef CURL_DISABLE_HSTS |
956 | if(data->hsts) { |
957 | outcurl->hsts = Curl_hsts_init(); |
958 | if(!outcurl->hsts) |
959 | goto fail; |
960 | if(outcurl->set.str[STRING_HSTS]) |
961 | (void)Curl_hsts_loadfile(outcurl, |
962 | outcurl->hsts, outcurl->set.str[STRING_HSTS]); |
963 | (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); |
964 | } |
965 | #endif |
966 | /* Clone the resolver handle, if present, for the new handle */ |
967 | if(Curl_resolver_duphandle(outcurl, |
968 | &outcurl->state.async.resolver, |
969 | data->state.async.resolver)) |
970 | goto fail; |
971 | |
972 | #ifdef USE_ARES |
973 | { |
974 | CURLcode rc; |
975 | |
976 | rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); |
977 | if(rc && rc != CURLE_NOT_BUILT_IN) |
978 | goto fail; |
979 | |
980 | rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); |
981 | if(rc && rc != CURLE_NOT_BUILT_IN) |
982 | goto fail; |
983 | |
984 | rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); |
985 | if(rc && rc != CURLE_NOT_BUILT_IN) |
986 | goto fail; |
987 | |
988 | rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); |
989 | if(rc && rc != CURLE_NOT_BUILT_IN) |
990 | goto fail; |
991 | } |
992 | #endif /* USE_ARES */ |
993 | |
994 | Curl_initinfo(outcurl); |
995 | |
996 | outcurl->magic = CURLEASY_MAGIC_NUMBER; |
997 | |
998 | /* we reach this point and thus we are OK */ |
999 | |
1000 | return outcurl; |
1001 | |
1002 | fail: |
1003 | |
1004 | if(outcurl) { |
1005 | #ifndef CURL_DISABLE_COOKIES |
1006 | curl_slist_free_all(outcurl->state.cookielist); |
1007 | outcurl->state.cookielist = NULL; |
1008 | #endif |
1009 | Curl_safefree(outcurl->state.buffer); |
1010 | Curl_dyn_free(&outcurl->state.headerb); |
1011 | Curl_safefree(outcurl->state.url); |
1012 | Curl_safefree(outcurl->state.referer); |
1013 | Curl_altsvc_cleanup(&outcurl->asi); |
1014 | Curl_hsts_cleanup(&outcurl->hsts); |
1015 | Curl_freeset(outcurl); |
1016 | free(outcurl); |
1017 | } |
1018 | |
1019 | return NULL; |
1020 | } |
1021 | |
1022 | /* |
1023 | * curl_easy_reset() is an external interface that allows an app to re- |
1024 | * initialize a session handle to the default values. |
1025 | */ |
1026 | void curl_easy_reset(struct Curl_easy *data) |
1027 | { |
1028 | Curl_free_request_state(data); |
1029 | |
1030 | /* zero out UserDefined data: */ |
1031 | Curl_freeset(data); |
1032 | memset(&data->set, 0, sizeof(struct UserDefined)); |
1033 | (void)Curl_init_userdefined(data); |
1034 | |
1035 | /* zero out Progress data: */ |
1036 | memset(&data->progress, 0, sizeof(struct Progress)); |
1037 | |
1038 | /* zero out PureInfo data: */ |
1039 | Curl_initinfo(data); |
1040 | |
1041 | data->progress.flags |= PGRS_HIDE; |
1042 | data->state.current_speed = -1; /* init to negative == impossible */ |
1043 | data->state.retrycount = 0; /* reset the retry counter */ |
1044 | |
1045 | /* zero out authentication data: */ |
1046 | memset(&data->state.authhost, 0, sizeof(struct auth)); |
1047 | memset(&data->state.authproxy, 0, sizeof(struct auth)); |
1048 | |
1049 | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) |
1050 | Curl_http_auth_cleanup_digest(data); |
1051 | #endif |
1052 | } |
1053 | |
1054 | /* |
1055 | * curl_easy_pause() allows an application to pause or unpause a specific |
1056 | * transfer and direction. This function sets the full new state for the |
1057 | * current connection this easy handle operates on. |
1058 | * |
1059 | * NOTE: if you have the receiving paused and you call this function to remove |
1060 | * the pausing, you may get your write callback called at this point. |
1061 | * |
1062 | * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h |
1063 | * |
1064 | * NOTE: This is one of few API functions that are allowed to be called from |
1065 | * within a callback. |
1066 | */ |
1067 | CURLcode curl_easy_pause(struct Curl_easy *data, int action) |
1068 | { |
1069 | struct SingleRequest *k; |
1070 | CURLcode result = CURLE_OK; |
1071 | int oldstate; |
1072 | int newstate; |
1073 | |
1074 | if(!GOOD_EASY_HANDLE(data) || !data->conn) |
1075 | /* crazy input, don't continue */ |
1076 | return CURLE_BAD_FUNCTION_ARGUMENT; |
1077 | |
1078 | k = &data->req; |
1079 | oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); |
1080 | |
1081 | /* first switch off both pause bits then set the new pause bits */ |
1082 | newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | |
1083 | ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | |
1084 | ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); |
1085 | |
1086 | if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) { |
1087 | /* Not changing any pause state, return */ |
1088 | DEBUGF(infof(data, "pause: no change, early return" )); |
1089 | return CURLE_OK; |
1090 | } |
1091 | |
1092 | /* Unpause parts in active mime tree. */ |
1093 | if((k->keepon & ~newstate & KEEP_SEND_PAUSE) && |
1094 | (data->mstate == MSTATE_PERFORMING || |
1095 | data->mstate == MSTATE_RATELIMITING) && |
1096 | data->state.fread_func == (curl_read_callback) Curl_mime_read) { |
1097 | Curl_mime_unpause(data->state.in); |
1098 | } |
1099 | |
1100 | /* put it back in the keepon */ |
1101 | k->keepon = newstate; |
1102 | |
1103 | if(!(newstate & KEEP_RECV_PAUSE)) { |
1104 | Curl_http2_stream_pause(data, FALSE); |
1105 | |
1106 | if(data->state.tempcount) { |
1107 | /* there are buffers for sending that can be delivered as the receive |
1108 | pausing is lifted! */ |
1109 | unsigned int i; |
1110 | unsigned int count = data->state.tempcount; |
1111 | struct tempbuf writebuf[3]; /* there can only be three */ |
1112 | |
1113 | /* copy the structs to allow for immediate re-pausing */ |
1114 | for(i = 0; i < data->state.tempcount; i++) { |
1115 | writebuf[i] = data->state.tempwrite[i]; |
1116 | Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); |
1117 | } |
1118 | data->state.tempcount = 0; |
1119 | |
1120 | for(i = 0; i < count; i++) { |
1121 | /* even if one function returns error, this loops through and frees |
1122 | all buffers */ |
1123 | if(!result) |
1124 | result = Curl_client_write(data, writebuf[i].type, |
1125 | Curl_dyn_ptr(&writebuf[i].b), |
1126 | Curl_dyn_len(&writebuf[i].b)); |
1127 | Curl_dyn_free(&writebuf[i].b); |
1128 | } |
1129 | |
1130 | if(result) |
1131 | return result; |
1132 | } |
1133 | } |
1134 | |
1135 | #ifdef USE_HYPER |
1136 | if(!(newstate & KEEP_SEND_PAUSE)) { |
1137 | /* need to wake the send body waker */ |
1138 | if(data->hyp.send_body_waker) { |
1139 | hyper_waker_wake(data->hyp.send_body_waker); |
1140 | data->hyp.send_body_waker = NULL; |
1141 | } |
1142 | } |
1143 | #endif |
1144 | |
1145 | /* if there's no error and we're not pausing both directions, we want |
1146 | to have this handle checked soon */ |
1147 | if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != |
1148 | (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) { |
1149 | Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ |
1150 | |
1151 | /* reset the too-slow time keeper */ |
1152 | data->state.keeps_speed.tv_sec = 0; |
1153 | |
1154 | if(!data->state.tempcount) |
1155 | /* if not pausing again, force a recv/send check of this connection as |
1156 | the data might've been read off the socket already */ |
1157 | data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; |
1158 | if(data->multi) { |
1159 | if(Curl_update_timer(data->multi)) |
1160 | return CURLE_ABORTED_BY_CALLBACK; |
1161 | } |
1162 | } |
1163 | |
1164 | if(!data->state.done) |
1165 | /* This transfer may have been moved in or out of the bundle, update the |
1166 | corresponding socket callback, if used */ |
1167 | result = Curl_updatesocket(data); |
1168 | |
1169 | return result; |
1170 | } |
1171 | |
1172 | |
1173 | static CURLcode easy_connection(struct Curl_easy *data, |
1174 | curl_socket_t *sfd, |
1175 | struct connectdata **connp) |
1176 | { |
1177 | if(!data) |
1178 | return CURLE_BAD_FUNCTION_ARGUMENT; |
1179 | |
1180 | /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ |
1181 | if(!data->set.connect_only) { |
1182 | failf(data, "CONNECT_ONLY is required" ); |
1183 | return CURLE_UNSUPPORTED_PROTOCOL; |
1184 | } |
1185 | |
1186 | *sfd = Curl_getconnectinfo(data, connp); |
1187 | |
1188 | if(*sfd == CURL_SOCKET_BAD) { |
1189 | failf(data, "Failed to get recent socket" ); |
1190 | return CURLE_UNSUPPORTED_PROTOCOL; |
1191 | } |
1192 | |
1193 | return CURLE_OK; |
1194 | } |
1195 | |
1196 | /* |
1197 | * Receives data from the connected socket. Use after successful |
1198 | * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. |
1199 | * Returns CURLE_OK on success, error code on error. |
1200 | */ |
1201 | CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, |
1202 | size_t *n) |
1203 | { |
1204 | curl_socket_t sfd; |
1205 | CURLcode result; |
1206 | ssize_t n1; |
1207 | struct connectdata *c; |
1208 | |
1209 | if(Curl_is_in_callback(data)) |
1210 | return CURLE_RECURSIVE_API_CALL; |
1211 | |
1212 | result = easy_connection(data, &sfd, &c); |
1213 | if(result) |
1214 | return result; |
1215 | |
1216 | if(!data->conn) |
1217 | /* on first invoke, the transfer has been detached from the connection and |
1218 | needs to be reattached */ |
1219 | Curl_attach_connection(data, c); |
1220 | |
1221 | *n = 0; |
1222 | result = Curl_read(data, sfd, buffer, buflen, &n1); |
1223 | |
1224 | if(result) |
1225 | return result; |
1226 | |
1227 | *n = (size_t)n1; |
1228 | |
1229 | return CURLE_OK; |
1230 | } |
1231 | |
1232 | /* |
1233 | * Sends data over the connected socket. Use after successful |
1234 | * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. |
1235 | */ |
1236 | CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, |
1237 | size_t buflen, size_t *n) |
1238 | { |
1239 | curl_socket_t sfd; |
1240 | CURLcode result; |
1241 | ssize_t n1; |
1242 | struct connectdata *c = NULL; |
1243 | SIGPIPE_VARIABLE(pipe_st); |
1244 | |
1245 | if(Curl_is_in_callback(data)) |
1246 | return CURLE_RECURSIVE_API_CALL; |
1247 | |
1248 | result = easy_connection(data, &sfd, &c); |
1249 | if(result) |
1250 | return result; |
1251 | |
1252 | if(!data->conn) |
1253 | /* on first invoke, the transfer has been detached from the connection and |
1254 | needs to be reattached */ |
1255 | Curl_attach_connection(data, c); |
1256 | |
1257 | *n = 0; |
1258 | sigpipe_ignore(data, &pipe_st); |
1259 | result = Curl_write(data, sfd, buffer, buflen, &n1); |
1260 | sigpipe_restore(&pipe_st); |
1261 | |
1262 | if(n1 == -1) |
1263 | return CURLE_SEND_ERROR; |
1264 | |
1265 | /* detect EAGAIN */ |
1266 | if(!result && !n1) |
1267 | return CURLE_AGAIN; |
1268 | |
1269 | *n = (size_t)n1; |
1270 | |
1271 | return result; |
1272 | } |
1273 | |
1274 | /* |
1275 | * Wrapper to call functions in Curl_conncache_foreach() |
1276 | * |
1277 | * Returns always 0. |
1278 | */ |
1279 | static int conn_upkeep(struct Curl_easy *data, |
1280 | struct connectdata *conn, |
1281 | void *param) |
1282 | { |
1283 | /* Param is unused. */ |
1284 | (void)param; |
1285 | |
1286 | if(conn->handler->connection_check) { |
1287 | /* briefly attach the connection to this transfer for the purpose of |
1288 | checking it */ |
1289 | Curl_attach_connection(data, conn); |
1290 | |
1291 | /* Do a protocol-specific keepalive check on the connection. */ |
1292 | conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); |
1293 | /* detach the connection again */ |
1294 | Curl_detach_connection(data); |
1295 | } |
1296 | |
1297 | return 0; /* continue iteration */ |
1298 | } |
1299 | |
1300 | static CURLcode upkeep(struct conncache *conn_cache, void *data) |
1301 | { |
1302 | /* Loop over every connection and make connection alive. */ |
1303 | Curl_conncache_foreach(data, |
1304 | conn_cache, |
1305 | data, |
1306 | conn_upkeep); |
1307 | return CURLE_OK; |
1308 | } |
1309 | |
1310 | /* |
1311 | * Performs connection upkeep for the given session handle. |
1312 | */ |
1313 | CURLcode curl_easy_upkeep(struct Curl_easy *data) |
1314 | { |
1315 | /* Verify that we got an easy handle we can work with. */ |
1316 | if(!GOOD_EASY_HANDLE(data)) |
1317 | return CURLE_BAD_FUNCTION_ARGUMENT; |
1318 | |
1319 | if(data->multi_easy) { |
1320 | /* Use the common function to keep connections alive. */ |
1321 | return upkeep(&data->multi_easy->conn_cache, data); |
1322 | } |
1323 | else { |
1324 | /* No connections, so just return success */ |
1325 | return CURLE_OK; |
1326 | } |
1327 | } |
1328 | |