| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338 | 
							- /***************************************************************************
 
-  *                                  _   _ ____  _
 
-  *  Project                     ___| | | |  _ \| |
 
-  *                             / __| | | | |_) | |
 
-  *                            | (__| |_| |  _ <| |___
 
-  *                             \___|\___/|_| \_\_____|
 
-  *
 
-  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
 
-  *
 
-  * This software is licensed as described in the file COPYING, which
 
-  * you should have received as part of this distribution. The terms
 
-  * are also available at https://curl.se/docs/copyright.html.
 
-  *
 
-  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 
-  * copies of the Software, and permit persons to whom the Software is
 
-  * furnished to do so, under the terms of the COPYING file.
 
-  *
 
-  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 
-  * KIND, either express or implied.
 
-  *
 
-  * SPDX-License-Identifier: curl
 
-  *
 
-  ***************************************************************************/
 
- #include "curl_setup.h"
 
- #ifdef HAVE_NETINET_IN_H
 
- #include <netinet/in.h>
 
- #endif
 
- #ifdef HAVE_NETDB_H
 
- #include <netdb.h>
 
- #endif
 
- #ifdef HAVE_ARPA_INET_H
 
- #include <arpa/inet.h>
 
- #endif
 
- #ifdef HAVE_NET_IF_H
 
- #include <net/if.h>
 
- #endif
 
- #ifdef HAVE_SYS_IOCTL_H
 
- #include <sys/ioctl.h>
 
- #endif
 
- #ifdef HAVE_SYS_PARAM_H
 
- #include <sys/param.h>
 
- #endif
 
- #include "urldata.h"
 
- #include <curl/curl.h>
 
- #include "transfer.h"
 
- #include "vtls/vtls.h"
 
- #include "url.h"
 
- #include "getinfo.h"
 
- #include "hostip.h"
 
- #include "share.h"
 
- #include "strdup.h"
 
- #include "progress.h"
 
- #include "easyif.h"
 
- #include "multiif.h"
 
- #include "select.h"
 
- #include "cfilters.h"
 
- #include "sendf.h" /* for failf function prototype */
 
- #include "connect.h" /* for Curl_getconnectinfo */
 
- #include "slist.h"
 
- #include "mime.h"
 
- #include "amigaos.h"
 
- #include "macos.h"
 
- #include "warnless.h"
 
- #include "sigpipe.h"
 
- #include "vssh/ssh.h"
 
- #include "setopt.h"
 
- #include "http_digest.h"
 
- #include "system_win32.h"
 
- #include "http2.h"
 
- #include "dynbuf.h"
 
- #include "altsvc.h"
 
- #include "hsts.h"
 
- #include "easy_lock.h"
 
- /* The last 3 #include files should be in this order */
 
- #include "curl_printf.h"
 
- #include "curl_memory.h"
 
- #include "memdebug.h"
 
- /* true globals -- for curl_global_init() and curl_global_cleanup() */
 
- static unsigned int  initialized;
 
- static long          easy_init_flags;
 
- #ifdef GLOBAL_INIT_IS_THREADSAFE
 
- static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
 
- #define global_init_lock() curl_simple_lock_lock(&s_lock)
 
- #define global_init_unlock() curl_simple_lock_unlock(&s_lock)
 
- #else
 
- #define global_init_lock()
 
- #define global_init_unlock()
 
- #endif
 
- /*
 
-  * strdup (and other memory functions) is redefined in complicated
 
-  * ways, but at this point it must be defined as the system-supplied strdup
 
-  * so the callback pointer is initialized correctly.
 
-  */
 
- #if defined(_WIN32_WCE)
 
- #define system_strdup _strdup
 
- #elif !defined(HAVE_STRDUP)
 
- #define system_strdup Curl_strdup
 
- #else
 
- #define system_strdup strdup
 
- #endif
 
- #if defined(_MSC_VER) && defined(_DLL)
 
- #  pragma warning(push)
 
- #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
 
- #endif
 
- /*
 
-  * If a memory-using function (like curl_getenv) is used before
 
-  * curl_global_init() is called, we need to have these pointers set already.
 
-  */
 
- curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
 
- curl_free_callback Curl_cfree = (curl_free_callback)free;
 
- curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 
- curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 
- curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
 
- #if defined(_WIN32) && defined(UNICODE)
 
- curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
 
- #endif
 
- #if defined(_MSC_VER) && defined(_DLL)
 
- #  pragma warning(pop)
 
- #endif
 
- #ifdef DEBUGBUILD
 
- static char *leakpointer;
 
- #endif
 
- /**
 
-  * curl_global_init() globally initializes curl given a bitwise set of the
 
-  * different features of what to initialize.
 
-  */
 
- static CURLcode global_init(long flags, bool memoryfuncs)
 
- {
 
-   if(initialized++)
 
-     return CURLE_OK;
 
-   if(memoryfuncs) {
 
-     /* Setup the default memory functions here (again) */
 
-     Curl_cmalloc = (curl_malloc_callback)malloc;
 
-     Curl_cfree = (curl_free_callback)free;
 
-     Curl_crealloc = (curl_realloc_callback)realloc;
 
-     Curl_cstrdup = (curl_strdup_callback)system_strdup;
 
-     Curl_ccalloc = (curl_calloc_callback)calloc;
 
- #if defined(_WIN32) && defined(UNICODE)
 
-     Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
 
- #endif
 
-   }
 
-   if(Curl_trc_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(!Curl_ssl_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(Curl_win32_init(flags)) {
 
-     DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(Curl_amiga_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(Curl_macos_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(Curl_resolver_global_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
 
-     goto fail;
 
-   }
 
-   if(Curl_ssh_init()) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n"));
 
-     goto fail;
 
-   }
 
-   easy_init_flags = flags;
 
- #ifdef DEBUGBUILD
 
-   if(getenv("CURL_GLOBAL_INIT"))
 
-     /* alloc data that will leak if *cleanup() is not called! */
 
-     leakpointer = malloc(1);
 
- #endif
 
-   return CURLE_OK;
 
- fail:
 
-   initialized--; /* undo the increase */
 
-   return CURLE_FAILED_INIT;
 
- }
 
- /**
 
-  * curl_global_init() globally initializes curl given a bitwise set of the
 
-  * different features of what to initialize.
 
-  */
 
- CURLcode curl_global_init(long flags)
 
- {
 
-   CURLcode result;
 
-   global_init_lock();
 
-   result = global_init(flags, TRUE);
 
-   global_init_unlock();
 
-   return result;
 
- }
 
- /*
 
-  * curl_global_init_mem() globally initializes curl and also registers the
 
-  * user provided callback routines.
 
-  */
 
- CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
 
-                               curl_free_callback f, curl_realloc_callback r,
 
-                               curl_strdup_callback s, curl_calloc_callback c)
 
- {
 
-   CURLcode result;
 
-   /* Invalid input, return immediately */
 
-   if(!m || !f || !r || !s || !c)
 
-     return CURLE_FAILED_INIT;
 
-   global_init_lock();
 
-   if(initialized) {
 
-     /* Already initialized, do not do it again, but bump the variable anyway to
 
-        work like curl_global_init() and require the same amount of cleanup
 
-        calls. */
 
-     initialized++;
 
-     global_init_unlock();
 
-     return CURLE_OK;
 
-   }
 
-   /* set memory functions before global_init() in case it wants memory
 
-      functions */
 
-   Curl_cmalloc = m;
 
-   Curl_cfree = f;
 
-   Curl_cstrdup = s;
 
-   Curl_crealloc = r;
 
-   Curl_ccalloc = c;
 
-   /* Call the actual init function, but without setting */
 
-   result = global_init(flags, FALSE);
 
-   global_init_unlock();
 
-   return result;
 
- }
 
- /**
 
-  * curl_global_cleanup() globally cleanups curl, uses the value of
 
-  * "easy_init_flags" to determine what needs to be cleaned up and what does
 
-  * not.
 
-  */
 
- void curl_global_cleanup(void)
 
- {
 
-   global_init_lock();
 
-   if(!initialized) {
 
-     global_init_unlock();
 
-     return;
 
-   }
 
-   if(--initialized) {
 
-     global_init_unlock();
 
-     return;
 
-   }
 
-   Curl_ssl_cleanup();
 
-   Curl_resolver_global_cleanup();
 
- #ifdef _WIN32
 
-   Curl_win32_cleanup(easy_init_flags);
 
- #endif
 
-   Curl_amiga_cleanup();
 
-   Curl_ssh_cleanup();
 
- #ifdef DEBUGBUILD
 
-   free(leakpointer);
 
- #endif
 
-   easy_init_flags = 0;
 
-   global_init_unlock();
 
- }
 
- /**
 
-  * curl_global_trace() globally initializes curl logging.
 
-  */
 
- CURLcode curl_global_trace(const char *config)
 
- {
 
- #ifndef CURL_DISABLE_VERBOSE_STRINGS
 
-   CURLcode result;
 
-   global_init_lock();
 
-   result = Curl_trc_opt(config);
 
-   global_init_unlock();
 
-   return result;
 
- #else
 
-   (void)config;
 
-   return CURLE_OK;
 
- #endif
 
- }
 
- /*
 
-  * curl_global_sslset() globally initializes the SSL backend to use.
 
-  */
 
- CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
 
-                               const curl_ssl_backend ***avail)
 
- {
 
-   CURLsslset rc;
 
-   global_init_lock();
 
-   rc = Curl_init_sslset_nolock(id, name, avail);
 
-   global_init_unlock();
 
-   return rc;
 
- }
 
- /*
 
-  * curl_easy_init() is the external interface to alloc, setup and init an
 
-  * easy handle that is returned. If anything goes wrong, NULL is returned.
 
-  */
 
- CURL *curl_easy_init(void)
 
- {
 
-   CURLcode result;
 
-   struct Curl_easy *data;
 
-   /* Make sure we inited the global SSL stuff */
 
-   global_init_lock();
 
-   if(!initialized) {
 
-     result = global_init(CURL_GLOBAL_DEFAULT, TRUE);
 
-     if(result) {
 
-       /* something in the global init failed, return nothing */
 
-       DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
 
-       global_init_unlock();
 
-       return NULL;
 
-     }
 
-   }
 
-   global_init_unlock();
 
-   /* We use curl_open() with undefined URL so far */
 
-   result = Curl_open(&data);
 
-   if(result) {
 
-     DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
 
-     return NULL;
 
-   }
 
-   return data;
 
- }
 
- #ifdef DEBUGBUILD
 
- struct socketmonitor {
 
-   struct socketmonitor *next; /* the next node in the list or NULL */
 
-   struct pollfd socket; /* socket info of what to monitor */
 
- };
 
- struct events {
 
-   long ms;              /* timeout, run the timeout function when reached */
 
-   bool msbump;          /* set TRUE when timeout is set by callback */
 
-   int num_sockets;      /* number of nodes in the monitor list */
 
-   struct socketmonitor *list; /* list of sockets to monitor */
 
-   int running_handles;  /* store the returned number */
 
- };
 
- #define DEBUG_EV_POLL   0
 
- /* events_timer
 
-  *
 
-  * Callback that gets called with a new value when the timeout should be
 
-  * updated.
 
-  */
 
- static int events_timer(CURLM *multi,    /* multi handle */
 
-                         long timeout_ms, /* see above */
 
-                         void *userp)     /* private callback pointer */
 
- {
 
-   struct events *ev = userp;
 
-   (void)multi;
 
- #if DEBUG_EV_POLL
 
-   fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms);
 
- #endif
 
-   ev->ms = timeout_ms;
 
-   ev->msbump = TRUE;
 
-   return 0;
 
- }
 
- /* poll2cselect
 
-  *
 
-  * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones
 
-  */
 
- static int poll2cselect(int pollmask)
 
- {
 
-   int omask = 0;
 
-   if(pollmask & POLLIN)
 
-     omask |= CURL_CSELECT_IN;
 
-   if(pollmask & POLLOUT)
 
-     omask |= CURL_CSELECT_OUT;
 
-   if(pollmask & POLLERR)
 
-     omask |= CURL_CSELECT_ERR;
 
-   return omask;
 
- }
 
- /* socketcb2poll
 
-  *
 
-  * convert from libcurl' CURL_POLL_* bit definitions to poll()'s
 
-  */
 
- static short socketcb2poll(int pollmask)
 
- {
 
-   short omask = 0;
 
-   if(pollmask & CURL_POLL_IN)
 
-     omask |= POLLIN;
 
-   if(pollmask & CURL_POLL_OUT)
 
-     omask |= POLLOUT;
 
-   return omask;
 
- }
 
- /* events_socket
 
-  *
 
-  * Callback that gets called with information about socket activity to
 
-  * monitor.
 
-  */
 
- static int events_socket(CURL *easy,      /* easy handle */
 
-                          curl_socket_t s, /* socket */
 
-                          int what,        /* see above */
 
-                          void *userp,     /* private callback
 
-                                              pointer */
 
-                          void *socketp)   /* private socket
 
-                                              pointer */
 
- {
 
-   struct events *ev = userp;
 
-   struct socketmonitor *m;
 
-   struct socketmonitor *prev = NULL;
 
-   bool found = FALSE;
 
-   struct Curl_easy *data = easy;
 
- #if defined(CURL_DISABLE_VERBOSE_STRINGS)
 
-   (void) easy;
 
- #endif
 
-   (void)socketp;
 
-   m = ev->list;
 
-   while(m) {
 
-     if(m->socket.fd == s) {
 
-       found = TRUE;
 
-       if(what == CURL_POLL_REMOVE) {
 
-         struct socketmonitor *nxt = m->next;
 
-         /* remove this node from the list of monitored sockets */
 
-         if(prev)
 
-           prev->next = nxt;
 
-         else
 
-           ev->list = nxt;
 
-         free(m);
 
-         infof(data, "socket cb: socket %" FMT_SOCKET_T " REMOVED", s);
 
-       }
 
-       else {
 
-         /* The socket 's' is already being monitored, update the activity
 
-            mask. Convert from libcurl bitmask to the poll one. */
 
-         m->socket.events = socketcb2poll(what);
 
-         infof(data, "socket cb: socket %" FMT_SOCKET_T
 
-               " UPDATED as %s%s", s,
 
-               (what&CURL_POLL_IN) ? "IN" : "",
 
-               (what&CURL_POLL_OUT) ? "OUT" : "");
 
-       }
 
-       break;
 
-     }
 
-     prev = m;
 
-     m = m->next; /* move to next node */
 
-   }
 
-   if(!found) {
 
-     if(what == CURL_POLL_REMOVE) {
 
-       /* should not happen if our logic is correct, but is no drama. */
 
-       DEBUGF(infof(data, "socket cb: asked to REMOVE socket %"
 
-                    FMT_SOCKET_T "but not present!", s));
 
-       DEBUGASSERT(0);
 
-     }
 
-     else {
 
-       m = malloc(sizeof(struct socketmonitor));
 
-       if(m) {
 
-         m->next = ev->list;
 
-         m->socket.fd = s;
 
-         m->socket.events = socketcb2poll(what);
 
-         m->socket.revents = 0;
 
-         ev->list = m;
 
-         infof(data, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s,
 
-               (what&CURL_POLL_IN) ? "IN" : "",
 
-               (what&CURL_POLL_OUT) ? "OUT" : "");
 
-       }
 
-       else
 
-         return CURLE_OUT_OF_MEMORY;
 
-     }
 
-   }
 
-   return 0;
 
- }
 
- /*
 
-  * events_setup()
 
-  *
 
-  * Do the multi handle setups that only event-based transfers need.
 
-  */
 
- static void events_setup(struct Curl_multi *multi, struct events *ev)
 
- {
 
-   /* timer callback */
 
-   curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer);
 
-   curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev);
 
-   /* socket callback */
 
-   curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket);
 
-   curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev);
 
- }
 
- /* wait_or_timeout()
 
-  *
 
-  * waits for activity on any of the given sockets, or the timeout to trigger.
 
-  */
 
- static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 
- {
 
-   bool done = FALSE;
 
-   CURLMcode mcode = CURLM_OK;
 
-   CURLcode result = CURLE_OK;
 
-   while(!done) {
 
-     CURLMsg *msg;
 
-     struct socketmonitor *m;
 
-     struct pollfd *f;
 
-     struct pollfd fds[4];
 
-     int numfds = 0;
 
-     int pollrc;
 
-     int i;
 
-     struct curltime before;
 
-     /* populate the fds[] array */
 
-     for(m = ev->list, f = &fds[0]; m; m = m->next) {
 
-       f->fd = m->socket.fd;
 
-       f->events = m->socket.events;
 
-       f->revents = 0;
 
- #if DEBUG_EV_POLL
 
-       fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
 
- #endif
 
-       f++;
 
-       numfds++;
 
-     }
 
-     /* get the time stamp to use to figure out how long poll takes */
 
-     before = Curl_now();
 
-     if(numfds) {
 
-       /* wait for activity or timeout */
 
- #if DEBUG_EV_POLL
 
-       fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms);
 
- #endif
 
-       pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
 
- #if DEBUG_EV_POLL
 
-       fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n",
 
-               numfds, ev->ms, pollrc);
 
- #endif
 
-       if(pollrc < 0)
 
-         return CURLE_UNRECOVERABLE_POLL;
 
-     }
 
-     else {
 
- #if DEBUG_EV_POLL
 
-       fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms);
 
- #endif
 
-       pollrc = 0;
 
-       if(ev->ms > 0)
 
-         Curl_wait_ms(ev->ms);
 
-     }
 
-     ev->msbump = FALSE; /* reset here */
 
-     if(!pollrc) {
 
-       /* timeout! */
 
-       ev->ms = 0;
 
-       /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */
 
-       mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
 
-                                        &ev->running_handles);
 
-     }
 
-     else {
 
-       /* here pollrc is > 0 */
 
-       struct Curl_llist_node *e = Curl_llist_head(&multi->process);
 
-       struct Curl_easy *data;
 
-       DEBUGASSERT(e);
 
-       data = Curl_node_elem(e);
 
-       DEBUGASSERT(data);
 
-       /* loop over the monitored sockets to see which ones had activity */
 
-       for(i = 0; i < numfds; i++) {
 
-         if(fds[i].revents) {
 
-           /* socket activity, tell libcurl */
 
-           int act = poll2cselect(fds[i].revents); /* convert */
 
-           /* sending infof "randomly" to the first easy handle */
 
-           infof(data, "call curl_multi_socket_action(socket "
 
-                 "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd);
 
-           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
 
-                                            &ev->running_handles);
 
-         }
 
-       }
 
-       if(!ev->msbump && ev->ms >= 0) {
 
-         /* If nothing updated the timeout, we decrease it by the spent time.
 
-          * If it was updated, it has the new timeout time stored already.
 
-          */
 
-         timediff_t timediff = Curl_timediff(Curl_now(), before);
 
-         if(timediff > 0) {
 
- #if DEBUG_EV_POLL
 
-         fprintf(stderr, "poll timeout %ldms not updated, decrease by "
 
-                 "time spent %ldms\n", ev->ms, (long)timediff);
 
- #endif
 
-           if(timediff > ev->ms)
 
-             ev->ms = 0;
 
-           else
 
-             ev->ms -= (long)timediff;
 
-         }
 
-       }
 
-     }
 
-     if(mcode)
 
-       return CURLE_URL_MALFORMAT;
 
-     /* we do not really care about the "msgs_in_queue" value returned in the
 
-        second argument */
 
-     msg = curl_multi_info_read(multi, &pollrc);
 
-     if(msg) {
 
-       result = msg->data.result;
 
-       done = TRUE;
 
-     }
 
-   }
 
-   return result;
 
- }
 
- /* easy_events()
 
-  *
 
-  * Runs a transfer in a blocking manner using the events-based API
 
-  */
 
- static CURLcode easy_events(struct Curl_multi *multi)
 
- {
 
-   /* this struct is made static to allow it to be used after this function
 
-      returns and curl_multi_remove_handle() is called */
 
-   static struct events evs = {-1, FALSE, 0, NULL, 0};
 
-   /* if running event-based, do some further multi inits */
 
-   events_setup(multi, &evs);
 
-   return wait_or_timeout(multi, &evs);
 
- }
 
- #else /* DEBUGBUILD */
 
- /* when not built with debug, this function does not exist */
 
- #define easy_events(x) CURLE_NOT_BUILT_IN
 
- #endif
 
- static CURLcode easy_transfer(struct Curl_multi *multi)
 
- {
 
-   bool done = FALSE;
 
-   CURLMcode mcode = CURLM_OK;
 
-   CURLcode result = CURLE_OK;
 
-   while(!done && !mcode) {
 
-     int still_running = 0;
 
-     mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
 
-     if(!mcode)
 
-       mcode = curl_multi_perform(multi, &still_running);
 
-     /* only read 'still_running' if curl_multi_perform() return OK */
 
-     if(!mcode && !still_running) {
 
-       int rc;
 
-       CURLMsg *msg = curl_multi_info_read(multi, &rc);
 
-       if(msg) {
 
-         result = msg->data.result;
 
-         done = TRUE;
 
-       }
 
-     }
 
-   }
 
-   /* Make sure to return some kind of error if there was a multi problem */
 
-   if(mcode) {
 
-     result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
 
-       /* The other multi errors should never happen, so return
 
-          something suitably generic */
 
-       CURLE_BAD_FUNCTION_ARGUMENT;
 
-   }
 
-   return result;
 
- }
 
- /*
 
-  * easy_perform() is the external interface that performs a blocking
 
-  * transfer as previously setup.
 
-  *
 
-  * CONCEPT: This function creates a multi handle, adds the easy handle to it,
 
-  * runs curl_multi_perform() until the transfer is done, then detaches the
 
-  * easy handle, destroys the multi handle and returns the easy handle's return
 
-  * code.
 
-  *
 
-  * REALITY: it cannot just create and destroy the multi handle that easily. It
 
-  * needs to keep it around since if this easy handle is used again by this
 
-  * function, the same multi handle must be reused so that the same pools and
 
-  * caches can be used.
 
-  *
 
-  * DEBUG: if 'events' is set TRUE, this function will use a replacement engine
 
-  * instead of curl_multi_perform() and use curl_multi_socket_action().
 
-  */
 
- static CURLcode easy_perform(struct Curl_easy *data, bool events)
 
- {
 
-   struct Curl_multi *multi;
 
-   CURLMcode mcode;
 
-   CURLcode result = CURLE_OK;
 
-   SIGPIPE_VARIABLE(pipe_st);
 
-   if(!data)
 
-     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-   if(data->set.errorbuffer)
 
-     /* clear this as early as possible */
 
-     data->set.errorbuffer[0] = 0;
 
-   data->state.os_errno = 0;
 
-   if(data->multi) {
 
-     failf(data, "easy handle already used in multi handle");
 
-     return CURLE_FAILED_INIT;
 
-   }
 
-   if(data->multi_easy)
 
-     multi = data->multi_easy;
 
-   else {
 
-     /* this multi handle will only ever have a single easy handled attached
 
-        to it, so make it use minimal hashes */
 
-     multi = Curl_multi_handle(1, 3, 7);
 
-     if(!multi)
 
-       return CURLE_OUT_OF_MEMORY;
 
-   }
 
-   if(multi->in_callback)
 
-     return CURLE_RECURSIVE_API_CALL;
 
-   /* Copy the MAXCONNECTS option to the multi handle */
 
-   curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
 
-   data->multi_easy = NULL; /* pretend it does not exist */
 
-   mcode = curl_multi_add_handle(multi, data);
 
-   if(mcode) {
 
-     curl_multi_cleanup(multi);
 
-     if(mcode == CURLM_OUT_OF_MEMORY)
 
-       return CURLE_OUT_OF_MEMORY;
 
-     return CURLE_FAILED_INIT;
 
-   }
 
-   /* assign this after curl_multi_add_handle() */
 
-   data->multi_easy = multi;
 
-   sigpipe_init(&pipe_st);
 
-   sigpipe_apply(data, &pipe_st);
 
-   /* run the transfer */
 
-   result = events ? easy_events(multi) : easy_transfer(multi);
 
-   /* ignoring the return code is not nice, but atm we cannot really handle
 
-      a failure here, room for future improvement! */
 
-   (void)curl_multi_remove_handle(multi, data);
 
-   sigpipe_restore(&pipe_st);
 
-   /* The multi handle is kept alive, owned by the easy handle */
 
-   return result;
 
- }
 
- /*
 
-  * curl_easy_perform() is the external interface that performs a blocking
 
-  * transfer as previously setup.
 
-  */
 
- CURLcode curl_easy_perform(CURL *data)
 
- {
 
-   return easy_perform(data, FALSE);
 
- }
 
- #ifdef DEBUGBUILD
 
- /*
 
-  * curl_easy_perform_ev() is the external interface that performs a blocking
 
-  * transfer using the event-based API internally.
 
-  */
 
- CURLcode curl_easy_perform_ev(struct Curl_easy *data)
 
- {
 
-   return easy_perform(data, TRUE);
 
- }
 
- #endif
 
- /*
 
-  * curl_easy_cleanup() is the external interface to cleaning/freeing the given
 
-  * easy handle.
 
-  */
 
- void curl_easy_cleanup(CURL *ptr)
 
- {
 
-   struct Curl_easy *data = ptr;
 
-   if(GOOD_EASY_HANDLE(data)) {
 
-     SIGPIPE_VARIABLE(pipe_st);
 
-     sigpipe_ignore(data, &pipe_st);
 
-     Curl_close(&data);
 
-     sigpipe_restore(&pipe_st);
 
-   }
 
- }
 
- /*
 
-  * curl_easy_getinfo() is an external interface that allows an app to retrieve
 
-  * information from a performed transfer and similar.
 
-  */
 
- #undef curl_easy_getinfo
 
- CURLcode curl_easy_getinfo(CURL *data, CURLINFO info, ...)
 
- {
 
-   va_list arg;
 
-   void *paramp;
 
-   CURLcode result;
 
-   va_start(arg, info);
 
-   paramp = va_arg(arg, void *);
 
-   result = Curl_getinfo(data, info, paramp);
 
-   va_end(arg);
 
-   return result;
 
- }
 
- static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
 
- {
 
-   CURLcode result = CURLE_OK;
 
-   enum dupstring i;
 
-   enum dupblob j;
 
-   /* Copy src->set into dst->set first, then deal with the strings
 
-      afterwards */
 
-   dst->set = src->set;
 
-   Curl_mime_initpart(&dst->set.mimepost);
 
-   /* clear all dest string and blob pointers first, in case we error out
 
-      mid-function */
 
-   memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
 
-   memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
 
-   /* duplicate all strings */
 
-   for(i = (enum dupstring)0; i < STRING_LASTZEROTERMINATED; i++) {
 
-     result = Curl_setstropt(&dst->set.str[i], src->set.str[i]);
 
-     if(result)
 
-       return result;
 
-   }
 
-   /* duplicate all blobs */
 
-   for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
 
-     result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
 
-     if(result)
 
-       return result;
 
-   }
 
-   /* duplicate memory areas pointed to */
 
-   i = STRING_COPYPOSTFIELDS;
 
-   if(src->set.str[i]) {
 
-     if(src->set.postfieldsize == -1)
 
-       dst->set.str[i] = strdup(src->set.str[i]);
 
-     else
 
-       /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
 
-       dst->set.str[i] = Curl_memdup(src->set.str[i],
 
-                                     curlx_sotouz(src->set.postfieldsize));
 
-     if(!dst->set.str[i])
 
-       return CURLE_OUT_OF_MEMORY;
 
-     /* point to the new copy */
 
-     dst->set.postfields = dst->set.str[i];
 
-   }
 
-   /* Duplicate mime data. */
 
-   result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost);
 
-   if(src->set.resolve)
 
-     dst->state.resolve = dst->set.resolve;
 
-   return result;
 
- }
 
- /*
 
-  * curl_easy_duphandle() is an external interface to allow duplication of a
 
-  * given input easy handle. The returned handle will be a new working handle
 
-  * with all options set exactly as the input source handle.
 
-  */
 
- CURL *curl_easy_duphandle(CURL *d)
 
- {
 
-   struct Curl_easy *data = d;
 
-   struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
 
-   if(!outcurl)
 
-     goto fail;
 
-   /*
 
-    * We setup a few buffers we need. We should probably make them
 
-    * get setup on-demand in the code, as that would probably decrease
 
-    * the likeliness of us forgetting to init a buffer here in the future.
 
-    */
 
-   outcurl->set.buffer_size = data->set.buffer_size;
 
-   /* copy all userdefined values */
 
-   if(dupset(outcurl, data))
 
-     goto fail;
 
-   Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
 
-   Curl_netrc_init(&outcurl->state.netrc);
 
-   /* the connection pool is setup on demand */
 
-   outcurl->state.lastconnect_id = -1;
 
-   outcurl->state.recent_conn_id = -1;
 
-   outcurl->id = -1;
 
-   outcurl->progress.flags    = data->progress.flags;
 
-   outcurl->progress.callback = data->progress.callback;
 
- #ifndef CURL_DISABLE_COOKIES
 
-   outcurl->state.cookielist = NULL;
 
-   if(data->cookies && data->state.cookie_engine) {
 
-     /* If cookies are enabled in the parent handle, we enable them
 
-        in the clone as well! */
 
-     outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies,
 
-                                         data->set.cookiesession);
 
-     if(!outcurl->cookies)
 
-       goto fail;
 
-   }
 
-   if(data->state.cookielist) {
 
-     outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
 
-     if(!outcurl->state.cookielist)
 
-       goto fail;
 
-   }
 
- #endif
 
-   if(data->state.url) {
 
-     outcurl->state.url = strdup(data->state.url);
 
-     if(!outcurl->state.url)
 
-       goto fail;
 
-     outcurl->state.url_alloc = TRUE;
 
-   }
 
-   if(data->state.referer) {
 
-     outcurl->state.referer = strdup(data->state.referer);
 
-     if(!outcurl->state.referer)
 
-       goto fail;
 
-     outcurl->state.referer_alloc = TRUE;
 
-   }
 
-   /* Reinitialize an SSL engine for the new handle
 
-    * note: the engine name has already been copied by dupset */
 
-   if(outcurl->set.str[STRING_SSL_ENGINE]) {
 
-     if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
 
-       goto fail;
 
-   }
 
- #ifndef CURL_DISABLE_ALTSVC
 
-   if(data->asi) {
 
-     outcurl->asi = Curl_altsvc_init();
 
-     if(!outcurl->asi)
 
-       goto fail;
 
-     if(outcurl->set.str[STRING_ALTSVC])
 
-       (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
 
-   }
 
- #endif
 
- #ifndef CURL_DISABLE_HSTS
 
-   if(data->hsts) {
 
-     outcurl->hsts = Curl_hsts_init();
 
-     if(!outcurl->hsts)
 
-       goto fail;
 
-     if(outcurl->set.str[STRING_HSTS])
 
-       (void)Curl_hsts_loadfile(outcurl,
 
-                                outcurl->hsts, outcurl->set.str[STRING_HSTS]);
 
-     (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
 
-   }
 
- #endif
 
- #ifdef CURLRES_ASYNCH
 
-   /* Clone the resolver handle, if present, for the new handle */
 
-   if(Curl_resolver_duphandle(outcurl,
 
-                              &outcurl->state.async.resolver,
 
-                              data->state.async.resolver))
 
-     goto fail;
 
- #endif
 
- #ifdef USE_ARES
 
-   {
 
-     CURLcode rc;
 
-     rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
 
-     if(rc && rc != CURLE_NOT_BUILT_IN)
 
-       goto fail;
 
-     rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
 
-     if(rc && rc != CURLE_NOT_BUILT_IN)
 
-       goto fail;
 
-     rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
 
-     if(rc && rc != CURLE_NOT_BUILT_IN)
 
-       goto fail;
 
-     rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
 
-     if(rc && rc != CURLE_NOT_BUILT_IN)
 
-       goto fail;
 
-   }
 
- #endif /* USE_ARES */
 
- #ifndef CURL_DISABLE_HTTP
 
-   Curl_llist_init(&outcurl->state.httphdrs, NULL);
 
- #endif
 
-   Curl_initinfo(outcurl);
 
-   outcurl->magic = CURLEASY_MAGIC_NUMBER;
 
-   /* we reach this point and thus we are OK */
 
-   return outcurl;
 
- fail:
 
-   if(outcurl) {
 
- #ifndef CURL_DISABLE_COOKIES
 
-     free(outcurl->cookies);
 
- #endif
 
-     Curl_dyn_free(&outcurl->state.headerb);
 
-     Curl_altsvc_cleanup(&outcurl->asi);
 
-     Curl_hsts_cleanup(&outcurl->hsts);
 
-     Curl_freeset(outcurl);
 
-     free(outcurl);
 
-   }
 
-   return NULL;
 
- }
 
- /*
 
-  * curl_easy_reset() is an external interface that allows an app to re-
 
-  * initialize a session handle to the default values.
 
-  */
 
- void curl_easy_reset(CURL *d)
 
- {
 
-   struct Curl_easy *data = d;
 
-   Curl_req_hard_reset(&data->req, data);
 
-   /* zero out UserDefined data: */
 
-   Curl_freeset(data);
 
-   memset(&data->set, 0, sizeof(struct UserDefined));
 
-   (void)Curl_init_userdefined(data);
 
-   /* zero out Progress data: */
 
-   memset(&data->progress, 0, sizeof(struct Progress));
 
-   /* zero out PureInfo data: */
 
-   Curl_initinfo(data);
 
-   data->progress.flags |= PGRS_HIDE;
 
-   data->state.current_speed = -1; /* init to negative == impossible */
 
-   data->state.retrycount = 0;     /* reset the retry counter */
 
-   /* zero out authentication data: */
 
-   memset(&data->state.authhost, 0, sizeof(struct auth));
 
-   memset(&data->state.authproxy, 0, sizeof(struct auth));
 
- #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
 
-   Curl_http_auth_cleanup_digest(data);
 
- #endif
 
- }
 
- /*
 
-  * curl_easy_pause() allows an application to pause or unpause a specific
 
-  * transfer and direction. This function sets the full new state for the
 
-  * current connection this easy handle operates on.
 
-  *
 
-  * NOTE: if you have the receiving paused and you call this function to remove
 
-  * the pausing, you may get your write callback called at this point.
 
-  *
 
-  * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
 
-  *
 
-  * NOTE: This is one of few API functions that are allowed to be called from
 
-  * within a callback.
 
-  */
 
- CURLcode curl_easy_pause(CURL *d, int action)
 
- {
 
-   struct SingleRequest *k;
 
-   CURLcode result = CURLE_OK;
 
-   int oldstate;
 
-   int newstate;
 
-   bool recursive = FALSE;
 
-   bool keep_changed, unpause_read, not_all_paused;
 
-   struct Curl_easy *data = d;
 
-   if(!GOOD_EASY_HANDLE(data) || !data->conn)
 
-     /* crazy input, do not continue */
 
-     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-   if(Curl_is_in_callback(data))
 
-     recursive = TRUE;
 
-   k = &data->req;
 
-   oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
 
-   /* first switch off both pause bits then set the new pause bits */
 
-   newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) |
 
-     ((action & CURLPAUSE_RECV) ? KEEP_RECV_PAUSE : 0) |
 
-     ((action & CURLPAUSE_SEND) ? KEEP_SEND_PAUSE : 0);
 
-   keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate);
 
-   not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
 
-                    (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE);
 
-   unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
 
-                   (data->mstate == MSTATE_PERFORMING ||
 
-                    data->mstate == MSTATE_RATELIMITING));
 
-   /* Unpausing writes is detected on the next run in
 
-    * transfer.c:Curl_sendrecv(). This is because this may result
 
-    * in a transfer error if the application's callbacks fail */
 
-   /* Set the new keepon state, so it takes effect no matter what error
 
-    * may happen afterwards. */
 
-   k->keepon = newstate;
 
-   /* If not completely pausing both directions now, run again in any case. */
 
-   if(not_all_paused) {
 
-     Curl_expire(data, 0, EXPIRE_RUN_NOW);
 
-     /* reset the too-slow time keeper */
 
-     data->state.keeps_speed.tv_sec = 0;
 
-     /* Simulate socket events on next run for unpaused directions */
 
-     if(!(newstate & KEEP_SEND_PAUSE))
 
-       data->state.select_bits |= CURL_CSELECT_OUT;
 
-     if(!(newstate & KEEP_RECV_PAUSE))
 
-       data->state.select_bits |= CURL_CSELECT_IN;
 
-     /* On changes, tell application to update its timers. */
 
-     if(keep_changed && data->multi) {
 
-       if(Curl_update_timer(data->multi)) {
 
-         result = CURLE_ABORTED_BY_CALLBACK;
 
-         goto out;
 
-       }
 
-     }
 
-   }
 
-   if(unpause_read) {
 
-     result = Curl_creader_unpause(data);
 
-     if(result)
 
-       goto out;
 
-   }
 
-   if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) {
 
-     Curl_conn_ev_data_pause(data, FALSE);
 
-     result = Curl_cwriter_unpause(data);
 
-   }
 
- out:
 
-   if(!result && !data->state.done && keep_changed)
 
-     /* This transfer may have been moved in or out of the bundle, update the
 
-        corresponding socket callback, if used */
 
-     result = Curl_updatesocket(data);
 
-   if(recursive)
 
-     /* this might have called a callback recursively which might have set this
 
-        to false again on exit */
 
-     Curl_set_in_callback(data, TRUE);
 
-   return result;
 
- }
 
- static CURLcode easy_connection(struct Curl_easy *data,
 
-                                 struct connectdata **connp)
 
- {
 
-   curl_socket_t sfd;
 
-   if(!data)
 
-     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-   /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */
 
-   if(!data->set.connect_only) {
 
-     failf(data, "CONNECT_ONLY is required");
 
-     return CURLE_UNSUPPORTED_PROTOCOL;
 
-   }
 
-   sfd = Curl_getconnectinfo(data, connp);
 
-   if(sfd == CURL_SOCKET_BAD) {
 
-     failf(data, "Failed to get recent socket");
 
-     return CURLE_UNSUPPORTED_PROTOCOL;
 
-   }
 
-   return CURLE_OK;
 
- }
 
- /*
 
-  * Receives data from the connected socket. Use after successful
 
-  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
 
-  * Returns CURLE_OK on success, error code on error.
 
-  */
 
- CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
 
- {
 
-   CURLcode result;
 
-   ssize_t n1;
 
-   struct connectdata *c;
 
-   struct Curl_easy *data = d;
 
-   if(Curl_is_in_callback(data))
 
-     return CURLE_RECURSIVE_API_CALL;
 
-   result = easy_connection(data, &c);
 
-   if(result)
 
-     return result;
 
-   if(!data->conn)
 
-     /* on first invoke, the transfer has been detached from the connection and
 
-        needs to be reattached */
 
-     Curl_attach_connection(data, c);
 
-   *n = 0;
 
-   result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1);
 
-   if(result)
 
-     return result;
 
-   *n = (size_t)n1;
 
-   return CURLE_OK;
 
- }
 
- #ifndef CURL_DISABLE_WEBSOCKETS
 
- CURLcode Curl_connect_only_attach(struct Curl_easy *data)
 
- {
 
-   CURLcode result;
 
-   struct connectdata *c = NULL;
 
-   result = easy_connection(data, &c);
 
-   if(result)
 
-     return result;
 
-   if(!data->conn)
 
-     /* on first invoke, the transfer has been detached from the connection and
 
-        needs to be reattached */
 
-     Curl_attach_connection(data, c);
 
-   return CURLE_OK;
 
- }
 
- #endif /* !CURL_DISABLE_WEBSOCKETS */
 
- /*
 
-  * Sends data over the connected socket.
 
-  *
 
-  * This is the private internal version of curl_easy_send()
 
-  */
 
- CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
 
-                        size_t buflen, size_t *n)
 
- {
 
-   CURLcode result;
 
-   struct connectdata *c = NULL;
 
-   SIGPIPE_VARIABLE(pipe_st);
 
-   *n = 0;
 
-   result = easy_connection(data, &c);
 
-   if(result)
 
-     return result;
 
-   if(!data->conn)
 
-     /* on first invoke, the transfer has been detached from the connection and
 
-        needs to be reattached */
 
-     Curl_attach_connection(data, c);
 
-   sigpipe_ignore(data, &pipe_st);
 
-   result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n);
 
-   sigpipe_restore(&pipe_st);
 
-   if(result && result != CURLE_AGAIN)
 
-     return CURLE_SEND_ERROR;
 
-   return result;
 
- }
 
- /*
 
-  * Sends data over the connected socket. Use after successful
 
-  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
 
-  */
 
- CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n)
 
- {
 
-   size_t written = 0;
 
-   CURLcode result;
 
-   struct Curl_easy *data = d;
 
-   if(Curl_is_in_callback(data))
 
-     return CURLE_RECURSIVE_API_CALL;
 
-   result = Curl_senddata(data, buffer, buflen, &written);
 
-   *n = written;
 
-   return result;
 
- }
 
- /*
 
-  * Performs connection upkeep for the given session handle.
 
-  */
 
- CURLcode curl_easy_upkeep(CURL *d)
 
- {
 
-   struct Curl_easy *data = d;
 
-   /* Verify that we got an easy handle we can work with. */
 
-   if(!GOOD_EASY_HANDLE(data))
 
-     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-   if(Curl_is_in_callback(data))
 
-     return CURLE_RECURSIVE_API_CALL;
 
-   /* Use the common function to keep connections alive. */
 
-   return Curl_cpool_upkeep(data);
 
- }
 
 
  |