Files correlati : omento : Aggiornata cUrl alla ultima versione su Github: 7.56.1 git-svn-id: svn://10.65.10.50/branches/R_10_00@24202 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1155 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1155 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *                                  _   _ ____  _
 | |
|  *  Project                     ___| | | |  _ \| |
 | |
|  *                             / __| | | | |_) | |
 | |
|  *                            | (__| |_| |  _ <| |___
 | |
|  *                             \___|\___/|_| \_\_____|
 | |
|  *
 | |
|  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, 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.haxx.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.
 | |
|  *
 | |
|  ***************************************************************************/
 | |
| 
 | |
| #include "curl_setup.h"
 | |
| 
 | |
| /*
 | |
|  * See comment in curl_memory.h for the explanation of this sanity check.
 | |
|  */
 | |
| 
 | |
| #ifdef CURLX_NO_MEMORY_CALLBACKS
 | |
| #error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined"
 | |
| #endif
 | |
| 
 | |
| #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 "select.h"
 | |
| #include "sendf.h" /* for failf function prototype */
 | |
| #include "connect.h" /* for Curl_getconnectinfo */
 | |
| #include "slist.h"
 | |
| #include "amigaos.h"
 | |
| #include "non-ascii.h"
 | |
| #include "warnless.h"
 | |
| #include "multiif.h"
 | |
| #include "sigpipe.h"
 | |
| #include "ssh.h"
 | |
| #include "setopt.h"
 | |
| 
 | |
| /* The last 3 #include files should be in this order */
 | |
| #include "curl_printf.h"
 | |
| #include "curl_memory.h"
 | |
| #include "memdebug.h"
 | |
| 
 | |
| void Curl_version_init(void);
 | |
| 
 | |
| /* win32_cleanup() is for win32 socket cleanup functionality, the opposite
 | |
|    of win32_init() */
 | |
| static void win32_cleanup(void)
 | |
| {
 | |
| #ifdef USE_WINSOCK
 | |
|   WSACleanup();
 | |
| #endif
 | |
| #ifdef USE_WINDOWS_SSPI
 | |
|   Curl_sspi_global_cleanup();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* win32_init() performs win32 socket initialization to properly setup the
 | |
|    stack to allow networking */
 | |
| static CURLcode win32_init(void)
 | |
| {
 | |
| #ifdef USE_WINSOCK
 | |
|   WORD wVersionRequested;
 | |
|   WSADATA wsaData;
 | |
|   int res;
 | |
| 
 | |
| #if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
 | |
|   Error IPV6_requires_winsock2
 | |
| #endif
 | |
| 
 | |
|   wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
 | |
| 
 | |
|   res = WSAStartup(wVersionRequested, &wsaData);
 | |
| 
 | |
|   if(res != 0)
 | |
|     /* Tell the user that we couldn't find a useable */
 | |
|     /* winsock.dll.     */
 | |
|     return CURLE_FAILED_INIT;
 | |
| 
 | |
|   /* Confirm that the Windows Sockets DLL supports what we need.*/
 | |
|   /* Note that if the DLL supports versions greater */
 | |
|   /* than wVersionRequested, it will still return */
 | |
|   /* wVersionRequested in wVersion. wHighVersion contains the */
 | |
|   /* highest supported version. */
 | |
| 
 | |
|   if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
 | |
|      HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) {
 | |
|     /* Tell the user that we couldn't find a useable */
 | |
| 
 | |
|     /* winsock.dll. */
 | |
|     WSACleanup();
 | |
|     return CURLE_FAILED_INIT;
 | |
|   }
 | |
|   /* The Windows Sockets DLL is acceptable. Proceed. */
 | |
| #elif defined(USE_LWIPSOCK)
 | |
|   lwip_init();
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_WINDOWS_SSPI
 | |
|   {
 | |
|     CURLcode result = Curl_sspi_global_init();
 | |
|     if(result)
 | |
|       return result;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return CURLE_OK;
 | |
| }
 | |
| 
 | |
| /* true globals -- for curl_global_init() and curl_global_cleanup() */
 | |
| static unsigned int  initialized;
 | |
| static long          init_flags;
 | |
| 
 | |
| /*
 | |
|  * 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 curlx_strdup
 | |
| #else
 | |
| #define system_strdup strdup
 | |
| #endif
 | |
| 
 | |
| #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
 | |
| #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
 | |
| #endif
 | |
| 
 | |
| #ifndef __SYMBIAN32__
 | |
| /*
 | |
|  * 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_callback)_wcsdup;
 | |
| #endif
 | |
| #else
 | |
| /*
 | |
|  * Symbian OS doesn't support initialization to code in writable static data.
 | |
|  * Initialization will occur in the curl_global_init() call.
 | |
|  */
 | |
| curl_malloc_callback Curl_cmalloc;
 | |
| curl_free_callback Curl_cfree;
 | |
| curl_realloc_callback Curl_crealloc;
 | |
| curl_strdup_callback Curl_cstrdup;
 | |
| curl_calloc_callback Curl_ccalloc;
 | |
| #endif
 | |
| 
 | |
| #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
 | |
| #  pragma warning(default:4232) /* MSVC extension, dllimport identity */
 | |
| #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(flags & CURL_GLOBAL_SSL)
 | |
|     if(!Curl_ssl_init()) {
 | |
|       DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
 | |
|       return CURLE_FAILED_INIT;
 | |
|     }
 | |
| 
 | |
|   if(flags & CURL_GLOBAL_WIN32)
 | |
|     if(win32_init()) {
 | |
|       DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
 | |
|       return CURLE_FAILED_INIT;
 | |
|     }
 | |
| 
 | |
| #ifdef __AMIGA__
 | |
|   if(!Curl_amiga_init()) {
 | |
|     DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
 | |
|     return CURLE_FAILED_INIT;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
| #ifdef NETWARE
 | |
|   if(netware_init()) {
 | |
|     DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n"));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if(Curl_resolver_global_init()) {
 | |
|     DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
 | |
|     return CURLE_FAILED_INIT;
 | |
|   }
 | |
| 
 | |
|   (void)Curl_ipv6works();
 | |
| 
 | |
| #if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
 | |
|   if(libssh2_init(0)) {
 | |
|     DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
 | |
|     return CURLE_FAILED_INIT;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if(flags & CURL_GLOBAL_ACK_EINTR)
 | |
|     Curl_ack_eintr = 1;
 | |
| 
 | |
|   init_flags = flags;
 | |
| 
 | |
|   Curl_version_init();
 | |
| 
 | |
|   return CURLE_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * curl_global_init() globally initializes curl given a bitwise set of the
 | |
|  * different features of what to initialize.
 | |
|  */
 | |
| CURLcode curl_global_init(long flags)
 | |
| {
 | |
|   return global_init(flags, TRUE);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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)
 | |
| {
 | |
|   /* Invalid input, return immediately */
 | |
|   if(!m || !f || !r || !s || !c)
 | |
|     return CURLE_FAILED_INIT;
 | |
| 
 | |
|   if(initialized) {
 | |
|     /* Already initialized, don't do it again, but bump the variable anyway to
 | |
|        work like curl_global_init() and require the same amount of cleanup
 | |
|        calls. */
 | |
|     initialized++;
 | |
|     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 */
 | |
|   return global_init(flags, FALSE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * curl_global_cleanup() globally cleanups curl, uses the value of
 | |
|  * "init_flags" to determine what needs to be cleaned up and what doesn't.
 | |
|  */
 | |
| void curl_global_cleanup(void)
 | |
| {
 | |
|   if(!initialized)
 | |
|     return;
 | |
| 
 | |
|   if(--initialized)
 | |
|     return;
 | |
| 
 | |
|   Curl_global_host_cache_dtor();
 | |
| 
 | |
|   if(init_flags & CURL_GLOBAL_SSL)
 | |
|     Curl_ssl_cleanup();
 | |
| 
 | |
|   Curl_resolver_global_cleanup();
 | |
| 
 | |
|   if(init_flags & CURL_GLOBAL_WIN32)
 | |
|     win32_cleanup();
 | |
| 
 | |
|   Curl_amiga_cleanup();
 | |
| 
 | |
| #if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT)
 | |
|   (void)libssh2_exit();
 | |
| #endif
 | |
| 
 | |
|   init_flags  = 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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.
 | |
|  */
 | |
| struct Curl_easy *curl_easy_init(void)
 | |
| {
 | |
|   CURLcode result;
 | |
|   struct Curl_easy *data;
 | |
| 
 | |
|   /* Make sure we inited the global SSL stuff */
 | |
|   if(!initialized) {
 | |
|     result = curl_global_init(CURL_GLOBAL_DEFAULT);
 | |
|     if(result) {
 | |
|       /* something in the global init failed, return nothing */
 | |
|       DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
 | |
|       return NULL;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* 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 CURLDEBUG
 | |
| 
 | |
| 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 */
 | |
| };
 | |
| 
 | |
| /* events_timer
 | |
|  *
 | |
|  * Callback that gets called with a new value when the timeout should be
 | |
|  * updated.
 | |
|  */
 | |
| 
 | |
| static int events_timer(struct Curl_multi *multi,    /* multi handle */
 | |
|                         long timeout_ms, /* see above */
 | |
|                         void *userp)    /* private callback pointer */
 | |
| {
 | |
|   struct events *ev = userp;
 | |
|   (void)multi;
 | |
|   if(timeout_ms == -1)
 | |
|     /* timeout removed */
 | |
|     timeout_ms = 0;
 | |
|   else if(timeout_ms == 0)
 | |
|     /* timeout is already reached! */
 | |
|     timeout_ms = 1; /* trigger asap */
 | |
| 
 | |
|   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(struct Curl_easy *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;
 | |
| 
 | |
| #if defined(CURL_DISABLE_VERBOSE_STRINGS)
 | |
|   (void) easy;
 | |
| #endif
 | |
|   (void)socketp;
 | |
| 
 | |
|   m = ev->list;
 | |
|   while(m) {
 | |
|     if(m->socket.fd == s) {
 | |
| 
 | |
|       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);
 | |
|         m = nxt;
 | |
|         infof(easy, "socket cb: socket %d REMOVED\n", 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(easy, "socket cb: socket %d UPDATED as %s%s\n", s,
 | |
|               what&CURL_POLL_IN?"IN":"",
 | |
|               what&CURL_POLL_OUT?"OUT":"");
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     prev = m;
 | |
|     m = m->next; /* move to next node */
 | |
|   }
 | |
|   if(!m) {
 | |
|     if(what == CURL_POLL_REMOVE) {
 | |
|       /* this happens a bit too often, libcurl fix perhaps? */
 | |
|       /* fprintf(stderr,
 | |
|          "%s: socket %d asked to be REMOVED but not present!\n",
 | |
|                  __func__, s); */
 | |
|     }
 | |
|     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(easy, "socket cb: socket %d ADDED as %s%s\n", 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;
 | |
|     struct curltime after;
 | |
| 
 | |
|     /* 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;
 | |
|       /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */
 | |
|       f++;
 | |
|       numfds++;
 | |
|     }
 | |
| 
 | |
|     /* get the time stamp to use to figure out how long poll takes */
 | |
|     before = Curl_now();
 | |
| 
 | |
|     /* wait for activity or timeout */
 | |
|     pollrc = Curl_poll(fds, numfds, (int)ev->ms);
 | |
| 
 | |
|     after = Curl_now();
 | |
| 
 | |
|     ev->msbump = FALSE; /* reset here */
 | |
| 
 | |
|     if(0 == 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 if(pollrc > 0) {
 | |
|       /* 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 */
 | |
|           infof(multi->easyp, "call curl_multi_socket_action(socket %d)\n",
 | |
|                 fds[i].fd);
 | |
|           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
 | |
|                                            &ev->running_handles);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if(!ev->msbump) {
 | |
|         /* 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(after, before);
 | |
|         if(timediff > 0) {
 | |
|           if(timediff > ev->ms)
 | |
|             ev->ms = 0;
 | |
|           else
 | |
|             ev->ms -= (long)timediff;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|       return CURLE_RECV_ERROR;
 | |
| 
 | |
|     if(mcode)
 | |
|       return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */
 | |
| 
 | |
|     /* we don't 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 = {2, FALSE, 0, NULL, 0};
 | |
| 
 | |
|   /* if running event-based, do some further multi inits */
 | |
|   events_setup(multi, &evs);
 | |
| 
 | |
|   return wait_or_timeout(multi, &evs);
 | |
| }
 | |
| #else /* CURLDEBUG */
 | |
| /* when not built with debug, this function doesn't 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;
 | |
|   struct curltime before;
 | |
|   int without_fds = 0;  /* count number of consecutive returns from
 | |
|                            curl_multi_wait() without any filedescriptors */
 | |
| 
 | |
|   while(!done && !mcode) {
 | |
|     int still_running = 0;
 | |
|     int rc;
 | |
| 
 | |
|     before = Curl_now();
 | |
|     mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc);
 | |
| 
 | |
|     if(!mcode) {
 | |
|       if(!rc) {
 | |
|         struct curltime after = Curl_now();
 | |
| 
 | |
|         /* If it returns without any filedescriptor instantly, we need to
 | |
|            avoid busy-looping during periods where it has nothing particular
 | |
|            to wait for */
 | |
|         if(Curl_timediff(after, before) <= 10) {
 | |
|           without_fds++;
 | |
|           if(without_fds > 2) {
 | |
|             int sleep_ms = without_fds < 10 ? (1 << (without_fds - 1)) : 1000;
 | |
|             Curl_wait_ms(sleep_ms);
 | |
|           }
 | |
|         }
 | |
|         else
 | |
|           /* it wasn't "instant", restart counter */
 | |
|           without_fds = 0;
 | |
|       }
 | |
|       else
 | |
|         /* got file descriptor, restart counter */
 | |
|         without_fds = 0;
 | |
| 
 | |
|       mcode = curl_multi_perform(multi, &still_running);
 | |
|     }
 | |
| 
 | |
|     /* only read 'still_running' if curl_multi_perform() return OK */
 | |
|     if(!mcode && !still_running) {
 | |
|       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 can't 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 re-used 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->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);
 | |
|     if(!multi)
 | |
|       return CURLE_OUT_OF_MEMORY;
 | |
|     data->multi_easy = multi;
 | |
|   }
 | |
| 
 | |
|   /* Copy the MAXCONNECTS option to the multi handle */
 | |
|   curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   sigpipe_ignore(data, &pipe_st);
 | |
| 
 | |
|   /* assign this after curl_multi_add_handle() since that function checks for
 | |
|      it and rejects this handle otherwise */
 | |
|   data->multi = multi;
 | |
| 
 | |
|   /* run the transfer */
 | |
|   result = events ? easy_events(multi) : easy_transfer(multi);
 | |
| 
 | |
|   /* ignoring the return code isn't nice, but atm we can't 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(struct Curl_easy *data)
 | |
| {
 | |
|   return easy_perform(data, FALSE);
 | |
| }
 | |
| 
 | |
| #ifdef CURLDEBUG
 | |
| /*
 | |
|  * 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(struct Curl_easy *data)
 | |
| {
 | |
|   SIGPIPE_VARIABLE(pipe_st);
 | |
| 
 | |
|   if(!data)
 | |
|     return;
 | |
| 
 | |
|   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(struct Curl_easy *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;
 | |
| 
 | |
|   /* Copy src->set into dst->set first, then deal with the strings
 | |
|      afterwards */
 | |
|   dst->set = src->set;
 | |
| 
 | |
|   /* clear all string pointers first */
 | |
|   memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
 | |
| 
 | |
|   /* 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 memory areas pointed to */
 | |
|   i = STRING_COPYPOSTFIELDS;
 | |
|   if(src->set.postfieldsize && src->set.str[i]) {
 | |
|     /* 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];
 | |
|   }
 | |
| 
 | |
|   return CURLE_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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.
 | |
|  */
 | |
| struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
 | |
| {
 | |
|   struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
 | |
|   if(NULL == 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;
 | |
|   outcurl->state.buffer = malloc(outcurl->set.buffer_size + 1);
 | |
|   if(!outcurl->state.buffer)
 | |
|     goto fail;
 | |
| 
 | |
|   outcurl->state.headerbuff = malloc(HEADERSIZE);
 | |
|   if(!outcurl->state.headerbuff)
 | |
|     goto fail;
 | |
|   outcurl->state.headersize = HEADERSIZE;
 | |
| 
 | |
|   /* copy all userdefined values */
 | |
|   if(dupset(outcurl, data))
 | |
|     goto fail;
 | |
| 
 | |
|   /* the connection cache is setup on demand */
 | |
|   outcurl->state.conn_cache = NULL;
 | |
| 
 | |
|   outcurl->state.lastconnect = NULL;
 | |
| 
 | |
|   outcurl->progress.flags    = data->progress.flags;
 | |
|   outcurl->progress.callback = data->progress.callback;
 | |
| 
 | |
|   if(data->cookies) {
 | |
|     /* If cookies are enabled in the parent handle, we enable them
 | |
|        in the clone as well! */
 | |
|     outcurl->cookies = Curl_cookie_init(data,
 | |
|                                         data->cookies->filename,
 | |
|                                         outcurl->cookies,
 | |
|                                         data->set.cookiesession);
 | |
|     if(!outcurl->cookies)
 | |
|       goto fail;
 | |
|   }
 | |
| 
 | |
|   /* duplicate all values in 'change' */
 | |
|   if(data->change.cookielist) {
 | |
|     outcurl->change.cookielist =
 | |
|       Curl_slist_duplicate(data->change.cookielist);
 | |
|     if(!outcurl->change.cookielist)
 | |
|       goto fail;
 | |
|   }
 | |
| 
 | |
|   if(data->change.url) {
 | |
|     outcurl->change.url = strdup(data->change.url);
 | |
|     if(!outcurl->change.url)
 | |
|       goto fail;
 | |
|     outcurl->change.url_alloc = TRUE;
 | |
|   }
 | |
| 
 | |
|   if(data->change.referer) {
 | |
|     outcurl->change.referer = strdup(data->change.referer);
 | |
|     if(!outcurl->change.referer)
 | |
|       goto fail;
 | |
|     outcurl->change.referer_alloc = TRUE;
 | |
|   }
 | |
| 
 | |
|   /* Clone the resolver handle, if present, for the new handle */
 | |
|   if(Curl_resolver_duphandle(&outcurl->state.resolver,
 | |
|                              data->state.resolver))
 | |
|     goto fail;
 | |
| 
 | |
|   Curl_convert_setup(outcurl);
 | |
| 
 | |
|   Curl_initinfo(outcurl);
 | |
| 
 | |
|   outcurl->magic = CURLEASY_MAGIC_NUMBER;
 | |
| 
 | |
|   /* we reach this point and thus we are OK */
 | |
| 
 | |
|   return outcurl;
 | |
| 
 | |
|   fail:
 | |
| 
 | |
|   if(outcurl) {
 | |
|     curl_slist_free_all(outcurl->change.cookielist);
 | |
|     outcurl->change.cookielist = NULL;
 | |
|     Curl_safefree(outcurl->state.buffer);
 | |
|     Curl_safefree(outcurl->state.headerbuff);
 | |
|     Curl_safefree(outcurl->change.url);
 | |
|     Curl_safefree(outcurl->change.referer);
 | |
|     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(struct Curl_easy *data)
 | |
| {
 | |
|   Curl_safefree(data->state.pathbuffer);
 | |
| 
 | |
|   data->state.path = NULL;
 | |
| 
 | |
|   Curl_free_request_state(data);
 | |
| 
 | |
|   /* zero out UserDefined data: */
 | |
|   Curl_freeset(data);
 | |
|   memset(&data->set, 0, sizeof(struct UserDefined));
 | |
|   (void)Curl_init_userdefined(&data->set);
 | |
| 
 | |
|   /* 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 */
 | |
| 
 | |
|   /* zero out authentication data: */
 | |
|   memset(&data->state.authhost, 0, sizeof(struct auth));
 | |
|   memset(&data->state.authproxy, 0, sizeof(struct auth));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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
 | |
|  */
 | |
| CURLcode curl_easy_pause(struct Curl_easy *data, int action)
 | |
| {
 | |
|   struct SingleRequest *k = &data->req;
 | |
|   CURLcode result = CURLE_OK;
 | |
| 
 | |
|   /* first switch off both pause bits */
 | |
|   int newstate = k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
 | |
| 
 | |
|   /* set the new desired pause bits */
 | |
|   newstate |= ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
 | |
|     ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
 | |
| 
 | |
|   /* put it back in the keepon */
 | |
|   k->keepon = newstate;
 | |
| 
 | |
|   if(!(newstate & KEEP_RECV_PAUSE) && data->state.tempcount) {
 | |
|     /* there are buffers for sending that can be delivered as the receive
 | |
|        pausing is lifted! */
 | |
|     unsigned int i;
 | |
|     unsigned int count = data->state.tempcount;
 | |
|     struct tempbuf writebuf[3]; /* there can only be three */
 | |
| 
 | |
|     /* copy the structs to allow for immediate re-pausing */
 | |
|     for(i = 0; i < data->state.tempcount; i++) {
 | |
|       writebuf[i] = data->state.tempwrite[i];
 | |
|       data->state.tempwrite[i].buf = NULL;
 | |
|     }
 | |
|     data->state.tempcount = 0;
 | |
| 
 | |
|     for(i = 0; i < count; i++) {
 | |
|       /* even if one function returns error, this loops through and frees all
 | |
|          buffers */
 | |
|       if(!result)
 | |
|         result = Curl_client_chop_write(data->easy_conn,
 | |
|                                         writebuf[i].type,
 | |
|                                         writebuf[i].buf,
 | |
|                                         writebuf[i].len);
 | |
|       free(writebuf[i].buf);
 | |
|     }
 | |
|     if(result)
 | |
|       return result;
 | |
|   }
 | |
| 
 | |
|   /* if there's no error and we're not pausing both directions, we want
 | |
|      to have this handle checked soon */
 | |
|   if(!result &&
 | |
|      ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
 | |
|       (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) )
 | |
|     Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| static CURLcode easy_connection(struct Curl_easy *data,
 | |
|                                 curl_socket_t *sfd,
 | |
|                                 struct connectdata **connp)
 | |
| {
 | |
|   if(data == NULL)
 | |
|     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(struct Curl_easy *data, void *buffer, size_t buflen,
 | |
|                         size_t *n)
 | |
| {
 | |
|   curl_socket_t sfd;
 | |
|   CURLcode result;
 | |
|   ssize_t n1;
 | |
|   struct connectdata *c;
 | |
| 
 | |
|   result = easy_connection(data, &sfd, &c);
 | |
|   if(result)
 | |
|     return result;
 | |
| 
 | |
|   *n = 0;
 | |
|   result = Curl_read(c, sfd, buffer, buflen, &n1);
 | |
| 
 | |
|   if(result)
 | |
|     return result;
 | |
| 
 | |
|   *n = (size_t)n1;
 | |
| 
 | |
|   return CURLE_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Sends data over the connected socket. Use after successful
 | |
|  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
 | |
|  */
 | |
| CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
 | |
|                         size_t buflen, size_t *n)
 | |
| {
 | |
|   curl_socket_t sfd;
 | |
|   CURLcode result;
 | |
|   ssize_t n1;
 | |
|   struct connectdata *c = NULL;
 | |
| 
 | |
|   result = easy_connection(data, &sfd, &c);
 | |
|   if(result)
 | |
|     return result;
 | |
| 
 | |
|   *n = 0;
 | |
|   result = Curl_write(c, sfd, buffer, buflen, &n1);
 | |
| 
 | |
|   if(n1 == -1)
 | |
|     return CURLE_SEND_ERROR;
 | |
| 
 | |
|   /* detect EAGAIN */
 | |
|   if(!result && !n1)
 | |
|     return CURLE_AGAIN;
 | |
| 
 | |
|   *n = (size_t)n1;
 | |
| 
 | |
|   return result;
 | |
| }
 |