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