1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 #include "test.h"
23 #include "memdebug.h"
24 
25 static const char *HOSTHEADER = "Host: www.host.foo.com";
26 static const char *JAR = "log/jar506";
27 #define THREADS 2
28 
29 /* struct containing data of a thread */
30 struct Tdata {
31   CURLSH *share;
32   char *url;
33 };
34 
35 struct userdata {
36   const char *text;
37   int counter;
38 };
39 
40 static int locks[3];
41 
42 /* lock callback */
my_lock(CURL * handle,curl_lock_data data,curl_lock_access laccess,void * useptr)43 static void my_lock(CURL *handle, curl_lock_data data,
44                     curl_lock_access laccess, void *useptr)
45 {
46   const char *what;
47   struct userdata *user = (struct userdata *)useptr;
48   int locknum;
49 
50   (void)handle;
51   (void)laccess;
52 
53   switch(data) {
54     case CURL_LOCK_DATA_SHARE:
55       what = "share";
56       locknum = 0;
57       break;
58     case CURL_LOCK_DATA_DNS:
59       what = "dns";
60       locknum = 1;
61       break;
62     case CURL_LOCK_DATA_COOKIE:
63       what = "cookie";
64       locknum = 2;
65       break;
66     default:
67       fprintf(stderr, "lock: no such data: %d\n", (int)data);
68       return;
69   }
70 
71   /* detect locking of locked locks */
72   if(locks[locknum]) {
73     printf("lock: double locked %s\n", what);
74     return;
75   }
76   locks[locknum]++;
77 
78   printf("lock:   %-6s [%s]: %d\n", what, user->text, user->counter);
79   user->counter++;
80 }
81 
82 /* unlock callback */
my_unlock(CURL * handle,curl_lock_data data,void * useptr)83 static void my_unlock(CURL *handle, curl_lock_data data, void *useptr)
84 {
85   const char *what;
86   struct userdata *user = (struct userdata *)useptr;
87   int locknum;
88   (void)handle;
89   switch(data) {
90     case CURL_LOCK_DATA_SHARE:
91       what = "share";
92       locknum = 0;
93       break;
94     case CURL_LOCK_DATA_DNS:
95       what = "dns";
96       locknum = 1;
97       break;
98     case CURL_LOCK_DATA_COOKIE:
99       what = "cookie";
100       locknum = 2;
101       break;
102     default:
103       fprintf(stderr, "unlock: no such data: %d\n", (int)data);
104       return;
105   }
106 
107   /* detect unlocking of unlocked locks */
108   if(!locks[locknum]) {
109     printf("unlock: double unlocked %s\n", what);
110     return;
111   }
112   locks[locknum]--;
113 
114   printf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter);
115   user->counter++;
116 }
117 
118 
119 /* build host entry */
sethost(struct curl_slist * headers)120 static struct curl_slist *sethost(struct curl_slist *headers)
121 {
122   (void)headers;
123   return curl_slist_append(NULL, HOSTHEADER);
124 }
125 
126 
127 /* the dummy thread function */
fire(void * ptr)128 static void *fire(void *ptr)
129 {
130   CURLcode code;
131   struct curl_slist *headers;
132   struct Tdata *tdata = (struct Tdata*)ptr;
133   CURL *curl;
134 
135   curl = curl_easy_init();
136   if(!curl) {
137     fprintf(stderr, "curl_easy_init() failed\n");
138     return NULL;
139   }
140 
141   headers = sethost(NULL);
142   curl_easy_setopt(curl, CURLOPT_VERBOSE,    1L);
143   curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
144   curl_easy_setopt(curl, CURLOPT_URL,        tdata->url);
145   curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
146   printf("CURLOPT_SHARE\n");
147   curl_easy_setopt(curl, CURLOPT_SHARE, tdata->share);
148 
149   printf("PERFORM\n");
150   code = curl_easy_perform(curl);
151   if(code) {
152     int i = 0;
153     fprintf(stderr, "perform url '%s' repeat %d failed, curlcode %d\n",
154             tdata->url, i, (int)code);
155   }
156 
157   printf("CLEANUP\n");
158   curl_easy_cleanup(curl);
159   curl_slist_free_all(headers);
160 
161   return NULL;
162 }
163 
164 
165 /* build request url */
suburl(const char * base,int i)166 static char *suburl(const char *base, int i)
167 {
168   return curl_maprintf("%s%.4d", base, i);
169 }
170 
171 
172 /* test function */
test(char * URL)173 int test(char *URL)
174 {
175   int res;
176   CURLSHcode scode = CURLSHE_OK;
177   CURLcode code = CURLE_OK;
178   char *url = NULL;
179   struct Tdata tdata;
180   CURL *curl;
181   CURLSH *share;
182   struct curl_slist *headers = NULL;
183   struct curl_slist *cookies = NULL;
184   struct curl_slist *next_cookie = NULL;
185   int i;
186   struct userdata user;
187 
188   user.text = "Pigs in space";
189   user.counter = 0;
190 
191   printf("GLOBAL_INIT\n");
192   if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
193     fprintf(stderr, "curl_global_init() failed\n");
194     return TEST_ERR_MAJOR_BAD;
195   }
196 
197   /* prepare share */
198   printf("SHARE_INIT\n");
199   share = curl_share_init();
200   if(!share) {
201     fprintf(stderr, "curl_share_init() failed\n");
202     curl_global_cleanup();
203     return TEST_ERR_MAJOR_BAD;
204   }
205 
206   if(CURLSHE_OK == scode) {
207     printf("CURLSHOPT_LOCKFUNC\n");
208     scode = curl_share_setopt(share, CURLSHOPT_LOCKFUNC, my_lock);
209   }
210   if(CURLSHE_OK == scode) {
211     printf("CURLSHOPT_UNLOCKFUNC\n");
212     scode = curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, my_unlock);
213   }
214   if(CURLSHE_OK == scode) {
215     printf("CURLSHOPT_USERDATA\n");
216     scode = curl_share_setopt(share, CURLSHOPT_USERDATA, &user);
217   }
218   if(CURLSHE_OK == scode) {
219     printf("CURL_LOCK_DATA_COOKIE\n");
220     scode = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
221   }
222   if(CURLSHE_OK == scode) {
223     printf("CURL_LOCK_DATA_DNS\n");
224     scode = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
225   }
226 
227   if(CURLSHE_OK != scode) {
228     fprintf(stderr, "curl_share_setopt() failed\n");
229     curl_share_cleanup(share);
230     curl_global_cleanup();
231     return TEST_ERR_MAJOR_BAD;
232   }
233 
234   /* initial cookie manipulation */
235   curl = curl_easy_init();
236   if(!curl) {
237     fprintf(stderr, "curl_easy_init() failed\n");
238     curl_share_cleanup(share);
239     curl_global_cleanup();
240     return TEST_ERR_MAJOR_BAD;
241   }
242   printf("CURLOPT_SHARE\n");
243   test_setopt(curl, CURLOPT_SHARE,      share);
244   printf("CURLOPT_COOKIELIST injected_and_clobbered\n");
245   test_setopt(curl, CURLOPT_COOKIELIST,
246                "Set-Cookie: injected_and_clobbered=yes; "
247                "domain=host.foo.com; expires=Sat Feb 2 11:56:27 GMT 2030");
248   printf("CURLOPT_COOKIELIST ALL\n");
249   test_setopt(curl, CURLOPT_COOKIELIST, "ALL");
250   printf("CURLOPT_COOKIELIST session\n");
251   test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants");
252   printf("CURLOPT_COOKIELIST injected\n");
253   test_setopt(curl, CURLOPT_COOKIELIST,
254                "Set-Cookie: injected=yes; domain=host.foo.com; "
255                "expires=Sat Feb 2 11:56:27 GMT 2030");
256   printf("CURLOPT_COOKIELIST SESS\n");
257   test_setopt(curl, CURLOPT_COOKIELIST, "SESS");
258   printf("CLEANUP\n");
259   curl_easy_cleanup(curl);
260 
261 
262   res = 0;
263 
264   /* start treads */
265   for(i = 1; i <= THREADS; i++) {
266 
267     /* set thread data */
268     tdata.url   = suburl(URL, i); /* must be curl_free()d */
269     tdata.share = share;
270 
271     /* simulate thread, direct call of "thread" function */
272     printf("*** run %d\n",i);
273     fire(&tdata);
274 
275     curl_free(tdata.url);
276   }
277 
278 
279   /* fetch a another one and save cookies */
280   printf("*** run %d\n", i);
281   curl = curl_easy_init();
282   if(!curl) {
283     fprintf(stderr, "curl_easy_init() failed\n");
284     curl_share_cleanup(share);
285     curl_global_cleanup();
286     return TEST_ERR_MAJOR_BAD;
287   }
288 
289   url = suburl(URL, i);
290   headers = sethost(NULL);
291   test_setopt(curl, CURLOPT_HTTPHEADER, headers);
292   test_setopt(curl, CURLOPT_URL,        url);
293   printf("CURLOPT_SHARE\n");
294   test_setopt(curl, CURLOPT_SHARE,      share);
295   printf("CURLOPT_COOKIEJAR\n");
296   test_setopt(curl, CURLOPT_COOKIEJAR,  JAR);
297   printf("CURLOPT_COOKIELIST FLUSH\n");
298   test_setopt(curl, CURLOPT_COOKIELIST, "FLUSH");
299 
300   printf("PERFORM\n");
301   curl_easy_perform(curl);
302 
303   printf("CLEANUP\n");
304   curl_easy_cleanup(curl);
305   curl_free(url);
306   curl_slist_free_all(headers);
307 
308   /* load cookies */
309   curl = curl_easy_init();
310   if(!curl) {
311     fprintf(stderr, "curl_easy_init() failed\n");
312     curl_share_cleanup(share);
313     curl_global_cleanup();
314     return TEST_ERR_MAJOR_BAD;
315   }
316   url = suburl(URL, i);
317   headers = sethost(NULL);
318   test_setopt(curl, CURLOPT_HTTPHEADER, headers);
319   test_setopt(curl, CURLOPT_URL,        url);
320   printf("CURLOPT_SHARE\n");
321   test_setopt(curl, CURLOPT_SHARE,      share);
322   printf("CURLOPT_COOKIELIST ALL\n");
323   test_setopt(curl, CURLOPT_COOKIELIST, "ALL");
324   printf("CURLOPT_COOKIEJAR\n");
325   test_setopt(curl, CURLOPT_COOKIEFILE, JAR);
326   printf("CURLOPT_COOKIELIST RELOAD\n");
327   test_setopt(curl, CURLOPT_COOKIELIST, "RELOAD");
328 
329   code = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies);
330   if(code != CURLE_OK) {
331     fprintf(stderr, "curl_easy_getinfo() failed\n");
332     res = TEST_ERR_MAJOR_BAD;
333     goto test_cleanup;
334   }
335   printf("loaded cookies:\n");
336   if(!cookies) {
337     fprintf(stderr, "  reloading cookies from '%s' failed\n", JAR);
338     res = TEST_ERR_MAJOR_BAD;
339     goto test_cleanup;
340   }
341   printf("-----------------\n");
342   next_cookie = cookies;
343   while(next_cookie) {
344     printf("  %s\n", next_cookie->data);
345     next_cookie = next_cookie->next;
346   }
347   printf("-----------------\n");
348   curl_slist_free_all(cookies);
349 
350   /* try to free share, expect to fail because share is in use*/
351   printf("try SHARE_CLEANUP...\n");
352   scode = curl_share_cleanup(share);
353   if(scode == CURLSHE_OK) {
354     fprintf(stderr, "curl_share_cleanup succeed but error expected\n");
355     share = NULL;
356   }
357   else {
358     printf("SHARE_CLEANUP failed, correct\n");
359   }
360 
361 test_cleanup:
362 
363   /* clean up last handle */
364   printf("CLEANUP\n");
365   curl_easy_cleanup(curl);
366   curl_slist_free_all(headers);
367   curl_free(url);
368 
369   /* free share */
370   printf("SHARE_CLEANUP\n");
371   scode = curl_share_cleanup(share);
372   if(scode != CURLSHE_OK)
373     fprintf(stderr, "curl_share_cleanup failed, code errno %d\n",
374             (int)scode);
375 
376   printf("GLOBAL_CLEANUP\n");
377   curl_global_cleanup();
378 
379   return res;
380 }
381