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