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