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 32static void unit_stop(void) 33{ 34 curl_global_cleanup(); 35} 36 37static CURLcode unit_setup(void) 38{ 39 CURLcode res = CURLE_OK; 40 41 global_init(CURL_GLOBAL_ALL); 42 43 return res; 44} 45 46struct 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 /* whether we expect a permanent or non-permanent cache entry */ 55 bool permanent; 56 57 /* 0 to 9 addresses expected from hostcache */ 58 const char *address[10]; 59}; 60 61 62/* In builds without IPv6 support CURLOPT_RESOLVE should skip over those 63 addresses, so we have to do that as well. */ 64static const char skip = 0; 65#ifdef ENABLE_IPV6 66#define IPV6ONLY(x) x 67#else 68#define IPV6ONLY(x) &skip 69#endif 70 71/* CURLOPT_RESOLVE address parsing tests */ 72static const struct testcase tests[] = { 73 /* spaces aren't allowed, for now */ 74 { "test.com:80:127.0.0.1, 127.0.0.2", 75 "test.com", 80, TRUE, { NULL, } 76 }, 77 { "TEST.com:80:,,127.0.0.1,,,127.0.0.2,,,,::1,,,", 78 "test.com", 80, TRUE, { "127.0.0.1", "127.0.0.2", IPV6ONLY("::1"), } 79 }, 80 { "test.com:80:::1,127.0.0.1", 81 "test.com", 80, TRUE, { IPV6ONLY("::1"), "127.0.0.1", } 82 }, 83 { "test.com:80:[::1],127.0.0.1", 84 "test.com", 80, TRUE, { IPV6ONLY("::1"), "127.0.0.1", } 85 }, 86 { "test.com:80:::1", 87 "test.com", 80, TRUE, { IPV6ONLY("::1"), } 88 }, 89 { "test.com:80:[::1]", 90 "test.com", 80, TRUE, { IPV6ONLY("::1"), } 91 }, 92 { "test.com:80:127.0.0.1", 93 "test.com", 80, TRUE, { "127.0.0.1", } 94 }, 95 { "test.com:80:,127.0.0.1", 96 "test.com", 80, TRUE, { "127.0.0.1", } 97 }, 98 { "test.com:80:127.0.0.1,", 99 "test.com", 80, TRUE, { "127.0.0.1", } 100 }, 101 { "test.com:0:127.0.0.1", 102 "test.com", 0, TRUE, { "127.0.0.1", } 103 }, 104 { "+test.com:80:127.0.0.1,", 105 "test.com", 80, FALSE, { "127.0.0.1", } 106 }, 107}; 108 109UNITTEST_START 110{ 111 int i; 112 int testnum = sizeof(tests) / sizeof(struct testcase); 113 struct Curl_multi *multi = NULL; 114 struct Curl_easy *easy = NULL; 115 struct curl_slist *list = NULL; 116 117 for(i = 0; i < testnum; ++i) { 118 int j; 119 int addressnum = sizeof(tests[i].address) / sizeof(*tests[i].address); 120 struct Curl_addrinfo *addr; 121 struct Curl_dns_entry *dns; 122 void *entry_id; 123 bool problem = false; 124 easy = curl_easy_init(); 125 if(!easy) 126 goto error; 127 128 /* create a multi handle and add the easy handle to it so that the 129 hostcache is setup */ 130 multi = curl_multi_init(); 131 curl_multi_add_handle(multi, easy); 132 133 list = curl_slist_append(NULL, tests[i].optval); 134 if(!list) 135 goto error; 136 curl_easy_setopt(easy, CURLOPT_RESOLVE, list); 137 138 Curl_loadhostpairs(easy); 139 140 entry_id = (void *)aprintf("%s:%d", tests[i].host, tests[i].port); 141 if(!entry_id) 142 goto error; 143 dns = Curl_hash_pick(easy->dns.hostcache, entry_id, strlen(entry_id) + 1); 144 free(entry_id); 145 entry_id = NULL; 146 147 addr = dns ? dns->addr : NULL; 148 149 for(j = 0; j < addressnum; ++j) { 150 int port = 0; 151 char ipaddress[MAX_IPADR_LEN] = {0}; 152 153 if(!addr && !tests[i].address[j]) 154 break; 155 156 if(tests[i].address[j] == &skip) 157 continue; 158 159 if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen, 160 ipaddress, &port)) { 161 fprintf(stderr, "%s:%d tests[%d] failed. getaddressinfo failed.\n", 162 __FILE__, __LINE__, i); 163 problem = true; 164 break; 165 } 166 167 if(addr && !tests[i].address[j]) { 168 fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr " 169 "is %s but tests[%d].address[%d] is NULL.\n", 170 __FILE__, __LINE__, i, ipaddress, i, j); 171 problem = true; 172 break; 173 } 174 175 if(!addr && tests[i].address[j]) { 176 fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr " 177 "is NULL but tests[%d].address[%d] is %s.\n", 178 __FILE__, __LINE__, i, i, j, tests[i].address[j]); 179 problem = true; 180 break; 181 } 182 183 if(!curl_strequal(ipaddress, tests[i].address[j])) { 184 fprintf(stderr, "%s:%d tests[%d] failed. the retrieved addr " 185 "%s is not equal to tests[%d].address[%d] %s.\n", 186 __FILE__, __LINE__, i, ipaddress, i, j, tests[i].address[j]); 187 problem = true; 188 break; 189 } 190 191 if(port != tests[i].port) { 192 fprintf(stderr, "%s:%d tests[%d] failed. the retrieved port " 193 "for tests[%d].address[%d] is %d but tests[%d].port is %d.\n", 194 __FILE__, __LINE__, i, i, j, port, i, tests[i].port); 195 problem = true; 196 break; 197 } 198 199 if(dns->timestamp && tests[i].permanent) { 200 fprintf(stderr, "%s:%d tests[%d] failed. the timestamp is not zero " 201 "but tests[%d].permanent is TRUE\n", 202 __FILE__, __LINE__, i, i); 203 problem = true; 204 break; 205 } 206 207 if(dns->timestamp == 0 && !tests[i].permanent) { 208 fprintf(stderr, "%s:%d tests[%d] failed. the timestamp is zero " 209 "but tests[%d].permanent is FALSE\n", 210 __FILE__, __LINE__, i, i); 211 problem = true; 212 break; 213 } 214 215 addr = addr->ai_next; 216 } 217 218 curl_easy_cleanup(easy); 219 easy = NULL; 220 curl_multi_cleanup(multi); 221 multi = NULL; 222 curl_slist_free_all(list); 223 list = NULL; 224 225 if(problem) { 226 unitfail++; 227 continue; 228 } 229 } 230error: 231 curl_easy_cleanup(easy); 232 curl_multi_cleanup(multi); 233 curl_slist_free_all(list); 234} 235UNITTEST_STOP 236