openssl: guard against OOM on context creation
[platform/upstream/curl.git] / lib / conncache.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
10  *
11  * This software is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution. The terms
13  * are also available at https://curl.haxx.se/docs/copyright.html.
14  *
15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16  * copies of the Software, and permit persons to whom the Software is
17  * furnished to do so, under the terms of the COPYING file.
18  *
19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20  * KIND, either express or implied.
21  *
22  ***************************************************************************/
23
24 #include "curl_setup.h"
25
26 #include <curl/curl.h>
27
28 #include "urldata.h"
29 #include "url.h"
30 #include "progress.h"
31 #include "multiif.h"
32 #include "sendf.h"
33 #include "conncache.h"
34 #include "share.h"
35 #include "sigpipe.h"
36 #include "connect.h"
37
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42
43 #define HASHKEY_SIZE 128
44
45 static void conn_llist_dtor(void *user, void *element)
46 {
47   struct connectdata *conn = element;
48   (void)user;
49   conn->bundle = NULL;
50 }
51
52 static CURLcode bundle_create(struct connectbundle **bundlep)
53 {
54   DEBUGASSERT(*bundlep == NULL);
55   *bundlep = malloc(sizeof(struct connectbundle));
56   if(!*bundlep)
57     return CURLE_OUT_OF_MEMORY;
58
59   (*bundlep)->num_connections = 0;
60   (*bundlep)->multiuse = BUNDLE_UNKNOWN;
61
62   Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor);
63   return CURLE_OK;
64 }
65
66 static void bundle_destroy(struct connectbundle *bundle)
67 {
68   if(!bundle)
69     return;
70
71   Curl_llist_destroy(&bundle->conn_list, NULL);
72
73   free(bundle);
74 }
75
76 /* Add a connection to a bundle */
77 static void bundle_add_conn(struct connectbundle *bundle,
78                             struct connectdata *conn)
79 {
80   Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
81                          &conn->bundle_node);
82   conn->bundle = bundle;
83   bundle->num_connections++;
84 }
85
86 /* Remove a connection from a bundle */
87 static int bundle_remove_conn(struct connectbundle *bundle,
88                               struct connectdata *conn)
89 {
90   struct Curl_llist_element *curr;
91
92   curr = bundle->conn_list.head;
93   while(curr) {
94     if(curr->ptr == conn) {
95       Curl_llist_remove(&bundle->conn_list, curr, NULL);
96       bundle->num_connections--;
97       conn->bundle = NULL;
98       return 1; /* we removed a handle */
99     }
100     curr = curr->next;
101   }
102   DEBUGASSERT(0);
103   return 0;
104 }
105
106 static void free_bundle_hash_entry(void *freethis)
107 {
108   struct connectbundle *b = (struct connectbundle *) freethis;
109
110   bundle_destroy(b);
111 }
112
113 int Curl_conncache_init(struct conncache *connc, int size)
114 {
115   int rc;
116
117   /* allocate a new easy handle to use when closing cached connections */
118   connc->closure_handle = curl_easy_init();
119   if(!connc->closure_handle)
120     return 1; /* bad */
121
122   rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
123                       Curl_str_key_compare, free_bundle_hash_entry);
124   if(rc)
125     Curl_close(&connc->closure_handle);
126   else
127     connc->closure_handle->state.conn_cache = connc;
128
129   return rc;
130 }
131
132 void Curl_conncache_destroy(struct conncache *connc)
133 {
134   if(connc)
135     Curl_hash_destroy(&connc->hash);
136 }
137
138 /* creates a key to find a bundle for this connection */
139 static void hashkey(struct connectdata *conn, char *buf,
140                     size_t len,  /* something like 128 is fine */
141                     const char **hostp)
142 {
143   const char *hostname;
144   long port = conn->remote_port;
145
146 #ifndef CURL_DISABLE_PROXY
147   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
148     hostname = conn->http_proxy.host.name;
149     port = conn->port;
150   }
151   else
152 #endif
153     if(conn->bits.conn_to_host)
154       hostname = conn->conn_to_host.name;
155   else
156     hostname = conn->host.name;
157
158   if(hostp)
159     /* report back which name we used */
160     *hostp = hostname;
161
162   /* put the number first so that the hostname gets cut off if too long */
163   msnprintf(buf, len, "%ld%s", port, hostname);
164 }
165
166 /* Returns number of connections currently held in the connection cache.
167    Locks/unlocks the cache itself!
168 */
169 size_t Curl_conncache_size(struct Curl_easy *data)
170 {
171   size_t num;
172   CONNCACHE_LOCK(data);
173   num = data->state.conn_cache->num_conn;
174   CONNCACHE_UNLOCK(data);
175   return num;
176 }
177
178 /* Look up the bundle with all the connections to the same host this
179    connectdata struct is setup to use.
180
181    **NOTE**: When it returns, it holds the connection cache lock! */
182 struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
183                                                  struct conncache *connc,
184                                                  const char **hostp)
185 {
186   struct connectbundle *bundle = NULL;
187   CONNCACHE_LOCK(conn->data);
188   if(connc) {
189     char key[HASHKEY_SIZE];
190     hashkey(conn, key, sizeof(key), hostp);
191     bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
192   }
193
194   return bundle;
195 }
196
197 static bool conncache_add_bundle(struct conncache *connc,
198                                  char *key,
199                                  struct connectbundle *bundle)
200 {
201   void *p = Curl_hash_add(&connc->hash, key, strlen(key), bundle);
202
203   return p?TRUE:FALSE;
204 }
205
206 static void conncache_remove_bundle(struct conncache *connc,
207                                     struct connectbundle *bundle)
208 {
209   struct Curl_hash_iterator iter;
210   struct Curl_hash_element *he;
211
212   if(!connc)
213     return;
214
215   Curl_hash_start_iterate(&connc->hash, &iter);
216
217   he = Curl_hash_next_element(&iter);
218   while(he) {
219     if(he->ptr == bundle) {
220       /* The bundle is destroyed by the hash destructor function,
221          free_bundle_hash_entry() */
222       Curl_hash_delete(&connc->hash, he->key, he->key_len);
223       return;
224     }
225
226     he = Curl_hash_next_element(&iter);
227   }
228 }
229
230 CURLcode Curl_conncache_add_conn(struct conncache *connc,
231                                  struct connectdata *conn)
232 {
233   CURLcode result = CURLE_OK;
234   struct connectbundle *bundle = NULL;
235   struct Curl_easy *data = conn->data;
236
237   /* *find_bundle() locks the connection cache */
238   bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache, NULL);
239   if(!bundle) {
240     int rc;
241     char key[HASHKEY_SIZE];
242
243     result = bundle_create(&bundle);
244     if(result) {
245       goto unlock;
246     }
247
248     hashkey(conn, key, sizeof(key), NULL);
249     rc = conncache_add_bundle(data->state.conn_cache, key, bundle);
250
251     if(!rc) {
252       bundle_destroy(bundle);
253       result = CURLE_OUT_OF_MEMORY;
254       goto unlock;
255     }
256   }
257
258   bundle_add_conn(bundle, conn);
259   conn->connection_id = connc->next_connection_id++;
260   connc->num_conn++;
261
262   DEBUGF(infof(conn->data, "Added connection %ld. "
263                "The cache now contains %zu members\n",
264                conn->connection_id, connc->num_conn));
265
266   unlock:
267   CONNCACHE_UNLOCK(data);
268
269   return result;
270 }
271
272 /*
273  * Removes the connectdata object from the connection cache, but does *not*
274  * clear the conn->data association. The transfer still owns this connection.
275  *
276  * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
277  * already holds the lock or not.
278  */
279 void Curl_conncache_remove_conn(struct Curl_easy *data,
280                                 struct connectdata *conn, bool lock)
281 {
282   struct connectbundle *bundle = conn->bundle;
283   struct conncache *connc = data->state.conn_cache;
284
285   /* The bundle pointer can be NULL, since this function can be called
286      due to a failed connection attempt, before being added to a bundle */
287   if(bundle) {
288     if(lock) {
289       CONNCACHE_LOCK(data);
290     }
291     bundle_remove_conn(bundle, conn);
292     if(bundle->num_connections == 0)
293       conncache_remove_bundle(connc, bundle);
294     conn->bundle = NULL; /* removed from it */
295     if(connc) {
296       connc->num_conn--;
297       DEBUGF(infof(data, "The cache now contains %zu members\n",
298                    connc->num_conn));
299     }
300     if(lock) {
301       CONNCACHE_UNLOCK(data);
302     }
303   }
304 }
305
306 /* This function iterates the entire connection cache and calls the function
307    func() with the connection pointer as the first argument and the supplied
308    'param' argument as the other.
309
310    The conncache lock is still held when the callback is called. It needs it,
311    so that it can safely continue traversing the lists once the callback
312    returns.
313
314    Returns 1 if the loop was aborted due to the callback's return code.
315
316    Return 0 from func() to continue the loop, return 1 to abort it.
317  */
318 bool Curl_conncache_foreach(struct Curl_easy *data,
319                             struct conncache *connc,
320                             void *param,
321                             int (*func)(struct connectdata *conn, void *param))
322 {
323   struct Curl_hash_iterator iter;
324   struct Curl_llist_element *curr;
325   struct Curl_hash_element *he;
326
327   if(!connc)
328     return FALSE;
329
330   CONNCACHE_LOCK(data);
331   Curl_hash_start_iterate(&connc->hash, &iter);
332
333   he = Curl_hash_next_element(&iter);
334   while(he) {
335     struct connectbundle *bundle;
336
337     bundle = he->ptr;
338     he = Curl_hash_next_element(&iter);
339
340     curr = bundle->conn_list.head;
341     while(curr) {
342       /* Yes, we need to update curr before calling func(), because func()
343          might decide to remove the connection */
344       struct connectdata *conn = curr->ptr;
345       curr = curr->next;
346
347       if(1 == func(conn, param)) {
348         CONNCACHE_UNLOCK(data);
349         return TRUE;
350       }
351     }
352   }
353   CONNCACHE_UNLOCK(data);
354   return FALSE;
355 }
356
357 /* Return the first connection found in the cache. Used when closing all
358    connections.
359
360    NOTE: no locking is done here as this is presumably only done when cleaning
361    up a cache!
362 */
363 static struct connectdata *
364 conncache_find_first_connection(struct conncache *connc)
365 {
366   struct Curl_hash_iterator iter;
367   struct Curl_hash_element *he;
368   struct connectbundle *bundle;
369
370   Curl_hash_start_iterate(&connc->hash, &iter);
371
372   he = Curl_hash_next_element(&iter);
373   while(he) {
374     struct Curl_llist_element *curr;
375     bundle = he->ptr;
376
377     curr = bundle->conn_list.head;
378     if(curr) {
379       return curr->ptr;
380     }
381
382     he = Curl_hash_next_element(&iter);
383   }
384
385   return NULL;
386 }
387
388 /*
389  * Give ownership of a connection back to the connection cache. Might
390  * disconnect the oldest existing in there to make space.
391  *
392  * Return TRUE if stored, FALSE if closed.
393  */
394 bool Curl_conncache_return_conn(struct Curl_easy *data,
395                                 struct connectdata *conn)
396 {
397   /* data->multi->maxconnects can be negative, deal with it. */
398   size_t maxconnects =
399     (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
400     data->multi->maxconnects;
401   struct connectdata *conn_candidate = NULL;
402
403   conn->lastused = Curl_now(); /* it was used up until now */
404   if(maxconnects > 0 &&
405      Curl_conncache_size(data) > maxconnects) {
406     infof(data, "Connection cache is full, closing the oldest one.\n");
407
408     conn_candidate = Curl_conncache_extract_oldest(data);
409     if(conn_candidate) {
410       /* the winner gets the honour of being disconnected */
411       (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
412     }
413   }
414
415   return (conn_candidate == conn) ? FALSE : TRUE;
416
417 }
418
419 /*
420  * This function finds the connection in the connection bundle that has been
421  * unused for the longest time.
422  *
423  * Does not lock the connection cache!
424  *
425  * Returns the pointer to the oldest idle connection, or NULL if none was
426  * found.
427  */
428 struct connectdata *
429 Curl_conncache_extract_bundle(struct Curl_easy *data,
430                               struct connectbundle *bundle)
431 {
432   struct Curl_llist_element *curr;
433   timediff_t highscore = -1;
434   timediff_t score;
435   struct curltime now;
436   struct connectdata *conn_candidate = NULL;
437   struct connectdata *conn;
438
439   (void)data;
440
441   now = Curl_now();
442
443   curr = bundle->conn_list.head;
444   while(curr) {
445     conn = curr->ptr;
446
447     if(!CONN_INUSE(conn) && !conn->data) {
448       /* Set higher score for the age passed since the connection was used */
449       score = Curl_timediff(now, conn->lastused);
450
451       if(score > highscore) {
452         highscore = score;
453         conn_candidate = conn;
454       }
455     }
456     curr = curr->next;
457   }
458   if(conn_candidate) {
459     /* remove it to prevent another thread from nicking it */
460     bundle_remove_conn(bundle, conn_candidate);
461     data->state.conn_cache->num_conn--;
462     DEBUGF(infof(data, "The cache now contains %zu members\n",
463                  data->state.conn_cache->num_conn));
464     conn_candidate->data = data; /* associate! */
465   }
466
467   return conn_candidate;
468 }
469
470 /*
471  * This function finds the connection in the connection cache that has been
472  * unused for the longest time and extracts that from the bundle.
473  *
474  * Returns the pointer to the connection, or NULL if none was found.
475  */
476 struct connectdata *
477 Curl_conncache_extract_oldest(struct Curl_easy *data)
478 {
479   struct conncache *connc = data->state.conn_cache;
480   struct Curl_hash_iterator iter;
481   struct Curl_llist_element *curr;
482   struct Curl_hash_element *he;
483   timediff_t highscore =- 1;
484   timediff_t score;
485   struct curltime now;
486   struct connectdata *conn_candidate = NULL;
487   struct connectbundle *bundle;
488   struct connectbundle *bundle_candidate = NULL;
489
490   now = Curl_now();
491
492   CONNCACHE_LOCK(data);
493   Curl_hash_start_iterate(&connc->hash, &iter);
494
495   he = Curl_hash_next_element(&iter);
496   while(he) {
497     struct connectdata *conn;
498
499     bundle = he->ptr;
500
501     curr = bundle->conn_list.head;
502     while(curr) {
503       conn = curr->ptr;
504
505       if(!CONN_INUSE(conn) && !conn->data && !conn->bits.close &&
506          !conn->bits.connect_only) {
507         /* Set higher score for the age passed since the connection was used */
508         score = Curl_timediff(now, conn->lastused);
509
510         if(score > highscore) {
511           highscore = score;
512           conn_candidate = conn;
513           bundle_candidate = bundle;
514         }
515       }
516       curr = curr->next;
517     }
518
519     he = Curl_hash_next_element(&iter);
520   }
521   if(conn_candidate) {
522     /* remove it to prevent another thread from nicking it */
523     bundle_remove_conn(bundle_candidate, conn_candidate);
524     connc->num_conn--;
525     DEBUGF(infof(data, "The cache now contains %zu members\n",
526                  connc->num_conn));
527     conn_candidate->data = data; /* associate! */
528   }
529   CONNCACHE_UNLOCK(data);
530
531   return conn_candidate;
532 }
533
534 void Curl_conncache_close_all_connections(struct conncache *connc)
535 {
536   struct connectdata *conn;
537   char buffer[READBUFFER_MIN + 1];
538   if(!connc->closure_handle)
539     return;
540   connc->closure_handle->state.buffer = buffer;
541   connc->closure_handle->set.buffer_size = READBUFFER_MIN;
542
543   conn = conncache_find_first_connection(connc);
544   while(conn) {
545     SIGPIPE_VARIABLE(pipe_st);
546     conn->data = connc->closure_handle;
547
548     sigpipe_ignore(conn->data, &pipe_st);
549     /* This will remove the connection from the cache */
550     connclose(conn, "kill all");
551     Curl_conncache_remove_conn(conn->data, conn, TRUE);
552     (void)Curl_disconnect(connc->closure_handle, conn, FALSE);
553     sigpipe_restore(&pipe_st);
554
555     conn = conncache_find_first_connection(connc);
556   }
557
558   connc->closure_handle->state.buffer = NULL;
559   if(connc->closure_handle) {
560     SIGPIPE_VARIABLE(pipe_st);
561     sigpipe_ignore(connc->closure_handle, &pipe_st);
562
563     Curl_hostcache_clean(connc->closure_handle,
564                          connc->closure_handle->dns.hostcache);
565     Curl_close(&connc->closure_handle);
566     sigpipe_restore(&pipe_st);
567   }
568 }
569
570 #if 0
571 /* Useful for debugging the connection cache */
572 void Curl_conncache_print(struct conncache *connc)
573 {
574   struct Curl_hash_iterator iter;
575   struct Curl_llist_element *curr;
576   struct Curl_hash_element *he;
577
578   if(!connc)
579     return;
580
581   fprintf(stderr, "=Bundle cache=\n");
582
583   Curl_hash_start_iterate(connc->hash, &iter);
584
585   he = Curl_hash_next_element(&iter);
586   while(he) {
587     struct connectbundle *bundle;
588     struct connectdata *conn;
589
590     bundle = he->ptr;
591
592     fprintf(stderr, "%s -", he->key);
593     curr = bundle->conn_list->head;
594     while(curr) {
595       conn = curr->ptr;
596
597       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
598       curr = curr->next;
599     }
600     fprintf(stderr, "\n");
601
602     he = Curl_hash_next_element(&iter);
603   }
604 }
605 #endif