1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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
25static const char *HOSTHEADER = "Host: www.host.foo.com";
26static const char *JAR = "log/jar506";
27#define THREADS 2
28
29/* struct containing data of a thread */
30struct Tdata {
31  CURLSH *share;
32  char *url;
33};
34
35struct userdata {
36  char *text;
37  int counter;
38};
39
40int lock[3];
41
42/* lock callback */
43static 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(lock[locknum]) {
73    printf("lock: double locked %s\n", what);
74    return;
75  }
76  lock[locknum]++;
77
78  printf("lock:   %-6s [%s]: %d\n", what, user->text, user->counter);
79  user->counter++;
80}
81
82/* unlock callback */
83static 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(!lock[locknum]) {
109    printf("unlock: double unlocked %s\n", what);
110    return;
111  }
112  lock[locknum]--;
113
114  printf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter);
115  user->counter++;
116}
117
118
119/* build host entry */
120static 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 */
128static void *fire(void *ptr)
129{
130  CURLcode code;
131  struct curl_slist *headers;
132  struct Tdata *tdata = (struct Tdata*)ptr;
133  CURL *curl;
134  int i=0;
135
136  curl = curl_easy_init();
137  if(!curl) {
138    fprintf(stderr, "curl_easy_init() failed\n");
139    return NULL;
140  }
141
142  headers = sethost(NULL);
143  curl_easy_setopt(curl, CURLOPT_VERBOSE,    1L);
144  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
145  curl_easy_setopt(curl, CURLOPT_URL,        tdata->url);
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    fprintf(stderr, "perform url '%s' repeat %d failed, curlcode %d\n",
153            tdata->url, i, (int)code);
154  }
155
156  printf("CLEANUP\n");
157  curl_easy_cleanup(curl);
158  curl_slist_free_all(headers);
159
160  return NULL;
161}
162
163
164/* build request url */
165static char *suburl(const char *base, int i)
166{
167  return curl_maprintf("%s%.4d", base, i);
168}
169
170
171/* test function */
172int test(char *URL)
173{
174  int res;
175  CURLSHcode scode = CURLSHE_OK;
176  CURLcode code = CURLE_OK;
177  char *url = NULL;
178  struct Tdata tdata;
179  CURL *curl;
180  CURLSH *share;
181  struct curl_slist *headers = NULL;
182  struct curl_slist *cookies = NULL;
183  struct curl_slist *next_cookie = NULL;
184  int i;
185  struct userdata user;
186
187  user.text = (char *)"Pigs in space";
188  user.counter = 0;
189
190  printf("GLOBAL_INIT\n");
191  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
192    fprintf(stderr, "curl_global_init() failed\n");
193    return TEST_ERR_MAJOR_BAD;
194  }
195
196  /* prepare share */
197  printf("SHARE_INIT\n");
198  if((share = curl_share_init()) == NULL) {
199    fprintf(stderr, "curl_share_init() failed\n");
200    curl_global_cleanup();
201    return TEST_ERR_MAJOR_BAD;
202  }
203
204  if(CURLSHE_OK == scode) {
205    printf("CURLSHOPT_LOCKFUNC\n");
206    scode = curl_share_setopt(share, CURLSHOPT_LOCKFUNC, my_lock);
207  }
208  if(CURLSHE_OK == scode) {
209    printf("CURLSHOPT_UNLOCKFUNC\n");
210    scode = curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, my_unlock);
211  }
212  if(CURLSHE_OK == scode) {
213    printf("CURLSHOPT_USERDATA\n");
214    scode = curl_share_setopt(share, CURLSHOPT_USERDATA, &user);
215  }
216  if(CURLSHE_OK == scode) {
217    printf("CURL_LOCK_DATA_COOKIE\n");
218    scode = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
219  }
220  if(CURLSHE_OK == scode) {
221    printf("CURL_LOCK_DATA_DNS\n");
222    scode = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
223  }
224
225  if(CURLSHE_OK != scode) {
226    fprintf(stderr, "curl_share_setopt() failed\n");
227    curl_share_cleanup(share);
228    curl_global_cleanup();
229    return TEST_ERR_MAJOR_BAD;
230  }
231
232  /* initial cookie manipulation */
233  if((curl = curl_easy_init()) == NULL) {
234    fprintf(stderr, "curl_easy_init() failed\n");
235    curl_share_cleanup(share);
236    curl_global_cleanup();
237    return TEST_ERR_MAJOR_BAD;
238  }
239  printf("CURLOPT_SHARE\n");
240  test_setopt(curl, CURLOPT_SHARE,      share);
241  printf("CURLOPT_COOKIELIST injected_and_clobbered\n");
242  test_setopt(curl, CURLOPT_COOKIELIST,
243               "Set-Cookie: injected_and_clobbered=yes; "
244               "domain=host.foo.com; expires=Sat Feb 2 11:56:27 GMT 2030");
245  printf("CURLOPT_COOKIELIST ALL\n");
246  test_setopt(curl, CURLOPT_COOKIELIST, "ALL");
247  printf("CURLOPT_COOKIELIST session\n");
248  test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants");
249  printf("CURLOPT_COOKIELIST injected\n");
250  test_setopt(curl, CURLOPT_COOKIELIST,
251               "Set-Cookie: injected=yes; domain=host.foo.com; "
252               "expires=Sat Feb 2 11:56:27 GMT 2030");
253  printf("CURLOPT_COOKIELIST SESS\n");
254  test_setopt(curl, CURLOPT_COOKIELIST, "SESS");
255  printf("CLEANUP\n");
256  curl_easy_cleanup(curl);
257
258
259  res = 0;
260
261  /* start treads */
262  for(i=1; i<=THREADS; i++) {
263
264    /* set thread data */
265    tdata.url   = suburl(URL, i); /* must be curl_free()d */
266    tdata.share = share;
267
268    /* simulate thread, direct call of "thread" function */
269    printf("*** run %d\n",i);
270    fire(&tdata);
271
272    curl_free(tdata.url);
273  }
274
275
276  /* fetch a another one and save cookies */
277  printf("*** run %d\n", i);
278  if((curl = curl_easy_init()) == NULL) {
279    fprintf(stderr, "curl_easy_init() failed\n");
280    curl_share_cleanup(share);
281    curl_global_cleanup();
282    return TEST_ERR_MAJOR_BAD;
283  }
284
285  url = suburl(URL, i);
286  headers = sethost(NULL);
287  test_setopt(curl, CURLOPT_HTTPHEADER, headers);
288  test_setopt(curl, CURLOPT_URL,        url);
289  printf("CURLOPT_SHARE\n");
290  test_setopt(curl, CURLOPT_SHARE,      share);
291  printf("CURLOPT_COOKIEJAR\n");
292  test_setopt(curl, CURLOPT_COOKIEJAR,  JAR);
293  printf("CURLOPT_COOKIELIST FLUSH\n");
294  test_setopt(curl, CURLOPT_COOKIELIST, "FLUSH");
295
296  printf("PERFORM\n");
297  curl_easy_perform(curl);
298
299  printf("CLEANUP\n");
300  curl_easy_cleanup(curl);
301  curl_free(url);
302  curl_slist_free_all(headers);
303
304  /* load cookies */
305  if((curl = curl_easy_init()) == NULL) {
306    fprintf(stderr, "curl_easy_init() failed\n");
307    curl_share_cleanup(share);
308    curl_global_cleanup();
309    return TEST_ERR_MAJOR_BAD;
310  }
311  url = suburl(URL, i);
312  headers = sethost(NULL);
313  test_setopt(curl, CURLOPT_HTTPHEADER, headers);
314  test_setopt(curl, CURLOPT_URL,        url);
315  printf("CURLOPT_SHARE\n");
316  test_setopt(curl, CURLOPT_SHARE,      share);
317  printf("CURLOPT_COOKIELIST ALL\n");
318  test_setopt(curl, CURLOPT_COOKIELIST, "ALL");
319  printf("CURLOPT_COOKIEJAR\n");
320  test_setopt(curl, CURLOPT_COOKIEFILE, JAR);
321  printf("CURLOPT_COOKIELIST RELOAD\n");
322  test_setopt(curl, CURLOPT_COOKIELIST, "RELOAD");
323
324  code = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies);
325  if(code != CURLE_OK) {
326    fprintf(stderr, "curl_easy_getinfo() failed\n");
327    res = TEST_ERR_MAJOR_BAD;
328    goto test_cleanup;
329  }
330  printf("loaded cookies:\n");
331  if(!cookies) {
332    fprintf(stderr, "  reloading cookies from '%s' failed\n", JAR);
333    res = TEST_ERR_MAJOR_BAD;
334    goto test_cleanup;
335  }
336  printf("-----------------\n");
337  next_cookie = cookies;
338  while(next_cookie) {
339    printf("  %s\n", next_cookie->data);
340    next_cookie = next_cookie->next;
341  }
342  printf("-----------------\n");
343  curl_slist_free_all(cookies);
344
345  /* try to free share, expect to fail because share is in use*/
346  printf("try SHARE_CLEANUP...\n");
347  scode = curl_share_cleanup(share);
348  if(scode==CURLSHE_OK) {
349    fprintf(stderr, "curl_share_cleanup succeed but error expected\n");
350    share = NULL;
351  }
352  else {
353    printf("SHARE_CLEANUP failed, correct\n");
354  }
355
356test_cleanup:
357
358  /* clean up last handle */
359  printf("CLEANUP\n");
360  curl_easy_cleanup(curl);
361  curl_slist_free_all(headers);
362  curl_free(url);
363
364  /* free share */
365  printf("SHARE_CLEANUP\n");
366  scode = curl_share_cleanup(share);
367  if(scode!=CURLSHE_OK)
368    fprintf(stderr, "curl_share_cleanup failed, code errno %d\n",
369            (int)scode);
370
371  printf("GLOBAL_CLEANUP\n");
372  curl_global_cleanup();
373
374  return res;
375}
376
377