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