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