xref: /curl/tests/unit/unit1609.c (revision c0233a35)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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.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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 #include "curlcheck.h"
25 
26 #include "urldata.h"
27 #include "connect.h"
28 #include "share.h"
29 
30 #include "memdebug.h" /* LAST include file */
31 
unit_stop(void)32 static void unit_stop(void)
33 {
34   curl_global_cleanup();
35 }
36 
unit_setup(void)37 static CURLcode unit_setup(void)
38 {
39   CURLcode res = CURLE_OK;
40 
41   global_init(CURL_GLOBAL_ALL);
42 
43   return res;
44 }
45 
46 struct testcase {
47   /* host:port:address[,address]... */
48   const char *optval;
49 
50   /* lowercase host and port to retrieve the addresses from hostcache */
51   const char *host;
52   int port;
53 
54   /* 0 to 9 addresses expected from hostcache */
55   const char *address[10];
56 };
57 
58 
59 /* CURLOPT_RESOLVE address parsing test - to test the following defect fix:
60 
61  1) if there is already existing host:port pair in the DNS cache and
62  we call CURLOPT_RESOLVE, it should also replace addresses.
63  for example, if there is "test.com:80" with address "1.1.1.1"
64  and we called CURLOPT_RESOLVE with address "2.2.2.2", then DNS entry needs to
65  reflect that.
66 
67  2) when cached address is already there and close to expire, then by the
68  time request is made, it can get expired.  This happens because, when
69  we set address using CURLOPT_RESOLVE,
70  it usually marks as permanent (by setting timestamp to zero). However,
71  if address already exists
72 in the cache, then it does not mark it, but just leaves it as it is.
73  So we fixing this by timestamp to zero if address already exists too.
74 
75 Test:
76 
77  - insert new entry
78  - verify that timestamp is not zero
79  - call set options with CURLOPT_RESOLVE
80  - then, call Curl_loadhostpairs
81 
82  expected result: cached address has zero timestamp.
83 
84  - call set options with CURLOPT_RESOLVE with same host:port pair,
85    different address.
86  - then, call Curl_loadhostpairs
87 
88  expected result: cached address has zero timestamp and new address
89 */
90 
91 static const struct testcase tests[] = {
92   /* spaces aren't allowed, for now */
93   { "test.com:80:127.0.0.1",
94     "test.com", 80, { "127.0.0.1", }
95   },
96   { "test.com:80:127.0.0.2",
97     "test.com", 80, { "127.0.0.2", }
98   },
99 };
100 
101 UNITTEST_START
102 {
103   int i;
104   int testnum = sizeof(tests) / sizeof(struct testcase);
105   struct Curl_multi *multi = NULL;
106   struct Curl_easy *easy = NULL;
107   struct curl_slist *list = NULL;
108 
109 /* important: we setup cache outside of the loop
110   and also clean cache after the loop. In contrast,for example,
111   test 1607 sets up and cleans cache on each iteration. */
112 
113   for(i = 0; i < testnum; ++i) {
114     int j;
115     int addressnum = sizeof (tests[i].address) / sizeof (*tests[i].address);
116     struct Curl_addrinfo *addr;
117     struct Curl_dns_entry *dns;
118     void *entry_id;
119     bool problem = false;
120     easy = curl_easy_init();
121     if(!easy) {
122       curl_global_cleanup();
123       return CURLE_OUT_OF_MEMORY;
124     }
125     /* create a multi handle and add the easy handle to it so that the
126        hostcache is setup */
127     multi = curl_multi_init();
128     if(!multi)
129       goto error;
130     curl_multi_add_handle(multi, easy);
131 
132     list = curl_slist_append(NULL, tests[i].optval);
133     if(!list)
134       goto error;
135 
136     curl_easy_setopt(easy, CURLOPT_RESOLVE, list);
137 
138     if(Curl_loadhostpairs(easy))
139       goto error;
140 
141     entry_id = (void *)aprintf("%s:%d", tests[i].host, tests[i].port);
142     if(!entry_id)
143       goto error;
144 
145     dns = Curl_hash_pick(easy->dns.hostcache, entry_id, strlen(entry_id) + 1);
146     free(entry_id);
147     entry_id = NULL;
148 
149     addr = dns ? dns->addr : NULL;
150 
151     for(j = 0; j < addressnum; ++j) {
152       int port = 0;
153       char ipaddress[MAX_IPADR_LEN] = {0};
154 
155       if(!addr && !tests[i].address[j])
156         break;
157 
158       if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen,
159                                    ipaddress, &port)) {
160         fprintf(stderr, "%s:%d tests[%d] failed. Curl_addr2string failed.\n",
161                 __FILE__, __LINE__, i);
162         problem = true;
163         break;
164       }
165 
166       if(addr && !tests[i].address[j]) {
167         fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr "
168                 "is %s but tests[%d].address[%d] is NULL.\n",
169                 __FILE__, __LINE__, i, ipaddress, i, j);
170         problem = true;
171         break;
172       }
173 
174       if(!addr && tests[i].address[j]) {
175         fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr "
176                 "is NULL but tests[%d].address[%d] is %s.\n",
177                 __FILE__, __LINE__, i, i, j, tests[i].address[j]);
178         problem = true;
179         break;
180       }
181 
182       if(!curl_strequal(ipaddress, tests[i].address[j])) {
183         fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr "
184                 "%s is not equal to tests[%d].address[%d] %s.\n",
185                 __FILE__, __LINE__, i, ipaddress, i, j, tests[i].address[j]);
186         problem = true;
187         break;
188       }
189 
190       if(port != tests[i].port) {
191         fprintf(stderr, "%s:%d tests[%d] failed. the retrieved port "
192                 "for tests[%d].address[%d] is %d but tests[%d].port is %d.\n",
193                 __FILE__, __LINE__, i, i, j, port, i, tests[i].port);
194         problem = true;
195         break;
196       }
197 
198       addr = addr->ai_next;
199     }
200 
201     curl_easy_cleanup(easy);
202     easy = NULL;
203     curl_multi_cleanup(multi);
204     multi = NULL;
205     curl_slist_free_all(list);
206     list = NULL;
207 
208     if(problem) {
209       unitfail++;
210       continue;
211     }
212   }
213   goto unit_test_abort;
214 error:
215   curl_easy_cleanup(easy);
216   curl_multi_cleanup(multi);
217   curl_slist_free_all(list);
218 }
219 UNITTEST_STOP
220