conncache.c: fix possible dereference of null pointer
[platform/upstream/curl.git] / lib / conncache.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012, Linus Nielsen Feltzing, <linus@haxx.se>
9  * Copyright (C) 2012 - 2013, 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 http://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 "rawstr.h"
34 #include "bundles.h"
35 #include "conncache.h"
36
37 #include "curl_memory.h"
38 /* The last #include file should be: */
39 #include "memdebug.h"
40
41 static void free_bundle_hash_entry(void *freethis)
42 {
43   struct connectbundle *b = (struct connectbundle *) freethis;
44
45   Curl_bundle_destroy(b);
46 }
47
48 struct conncache *Curl_conncache_init(int size)
49 {
50   struct conncache *connc;
51
52   connc = calloc(1, sizeof(struct conncache));
53   if(!connc)
54     return NULL;
55
56   connc->hash = Curl_hash_alloc(size, Curl_hash_str,
57                                 Curl_str_key_compare, free_bundle_hash_entry);
58
59   if(!connc->hash) {
60     free(connc);
61     return NULL;
62   }
63
64   return connc;
65 }
66
67 void Curl_conncache_destroy(struct conncache *connc)
68 {
69   if(connc) {
70     Curl_hash_destroy(connc->hash);
71     connc->hash = NULL;
72     free(connc);
73   }
74 }
75
76 struct connectbundle *Curl_conncache_find_bundle(struct conncache *connc,
77                                                  char *hostname)
78 {
79   struct connectbundle *bundle = NULL;
80
81   if(connc)
82     bundle = Curl_hash_pick(connc->hash, hostname, strlen(hostname)+1);
83
84   return bundle;
85 }
86
87 static bool conncache_add_bundle(struct conncache *connc,
88                                  char *hostname,
89                                  struct connectbundle *bundle)
90 {
91   void *p;
92
93   p = Curl_hash_add(connc->hash, hostname, strlen(hostname)+1, bundle);
94
95   return p?TRUE:FALSE;
96 }
97
98 static void conncache_remove_bundle(struct conncache *connc,
99                                     struct connectbundle *bundle)
100 {
101   struct curl_hash_iterator iter;
102   struct curl_hash_element *he;
103
104   if(!connc)
105     return;
106
107   Curl_hash_start_iterate(connc->hash, &iter);
108
109   he = Curl_hash_next_element(&iter);
110   while(he) {
111     if(he->ptr == bundle) {
112       /* The bundle is destroyed by the hash destructor function,
113          free_bundle_hash_entry() */
114       Curl_hash_delete(connc->hash, he->key, he->key_len);
115       return;
116     }
117
118     he = Curl_hash_next_element(&iter);
119   }
120 }
121
122 CURLcode Curl_conncache_add_conn(struct conncache *connc,
123                                  struct connectdata *conn)
124 {
125   CURLcode result;
126   struct connectbundle *bundle;
127   struct connectbundle *new_bundle = NULL;
128   struct SessionHandle *data = conn->data;
129
130   bundle = Curl_conncache_find_bundle(data->state.conn_cache,
131                                       conn->host.name);
132   if(!bundle) {
133     result = Curl_bundle_create(data, &new_bundle);
134     if(result != CURLE_OK)
135       return result;
136
137     if(!conncache_add_bundle(data->state.conn_cache,
138                              conn->host.name, new_bundle)) {
139       Curl_bundle_destroy(new_bundle);
140       return CURLE_OUT_OF_MEMORY;
141     }
142     bundle = new_bundle;
143   }
144
145   result = Curl_bundle_add_conn(bundle, conn);
146   if(result != CURLE_OK) {
147     if(new_bundle)
148       conncache_remove_bundle(data->state.conn_cache, new_bundle);
149     return result;
150   }
151
152   connc->num_connections++;
153
154   return CURLE_OK;
155 }
156
157 void Curl_conncache_remove_conn(struct conncache *connc,
158                                 struct connectdata *conn)
159 {
160   struct connectbundle *bundle = conn->bundle;
161
162   /* The bundle pointer can be NULL, since this function can be called
163      due to a failed connection attempt, before being added to a bundle */
164   if(bundle) {
165     Curl_bundle_remove_conn(bundle, conn);
166     if(bundle->num_connections == 0) {
167       conncache_remove_bundle(connc, bundle);
168     }
169
170     if(connc) {
171       connc->num_connections--;
172
173       DEBUGF(infof(conn->data, "The cache now contains %d members\n",
174                    connc->num_connections));
175     }
176   }
177 }
178
179 /* This function iterates the entire connection cache and calls the
180    function func() with the connection pointer as the first argument
181    and the supplied 'param' argument as the other,
182
183    Return 0 from func() to continue the loop, return 1 to abort it.
184  */
185 void Curl_conncache_foreach(struct conncache *connc,
186                             void *param,
187                             int (*func)(struct connectdata *conn, void *param))
188 {
189   struct curl_hash_iterator iter;
190   struct curl_llist_element *curr;
191   struct curl_hash_element *he;
192
193   if(!connc)
194     return;
195
196   Curl_hash_start_iterate(connc->hash, &iter);
197
198   he = Curl_hash_next_element(&iter);
199   while(he) {
200     struct connectbundle *bundle;
201     struct connectdata *conn;
202
203     bundle = he->ptr;
204
205     curr = bundle->conn_list->head;
206     while(curr) {
207       /* Yes, we need to update curr before calling func(), because func()
208          might decide to remove the connection */
209       conn = curr->ptr;
210       curr = curr->next;
211
212       if(1 == func(conn, param))
213         return;
214     }
215
216     he = Curl_hash_next_element(&iter);
217   }
218 }
219
220 /* Return the first connection found in the cache. Used when closing all
221    connections */
222 struct connectdata *
223 Curl_conncache_find_first_connection(struct conncache *connc)
224 {
225   struct curl_hash_iterator iter;
226   struct curl_llist_element *curr;
227   struct curl_hash_element *he;
228   struct connectbundle *bundle;
229
230   Curl_hash_start_iterate(connc->hash, &iter);
231
232   he = Curl_hash_next_element(&iter);
233   while(he) {
234     bundle = he->ptr;
235
236     curr = bundle->conn_list->head;
237     if(curr) {
238       return curr->ptr;
239     }
240
241     he = Curl_hash_next_element(&iter);
242   }
243
244   return NULL;
245 }
246
247
248 #if 0
249 /* Useful for debugging the connection cache */
250 void Curl_conncache_print(struct conncache *connc)
251 {
252   struct curl_hash_iterator iter;
253   struct curl_llist_element *curr;
254   struct curl_hash_element *he;
255
256   if(!connc)
257     return;
258
259   fprintf(stderr, "=Bundle cache=\n");
260
261   Curl_hash_start_iterate(connc->hash, &iter);
262
263   he = Curl_hash_next_element(&iter);
264   while(he) {
265     struct connectbundle *bundle;
266     struct connectdata *conn;
267
268     bundle = he->ptr;
269
270     fprintf(stderr, "%s -", he->key);
271     curr = bundle->conn_list->head;
272     while(curr) {
273       conn = curr->ptr;
274
275       fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
276       curr = curr->next;
277     }
278     fprintf(stderr, "\n");
279
280     he = Curl_hash_next_element(&iter);
281   }
282 }
283 #endif