unit2600.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. #ifdef HAVE_NETINET_IN_H
  26. #include <netinet/in.h>
  27. #endif
  28. #ifdef HAVE_NETINET_IN6_H
  29. #include <netinet/in6.h>
  30. #endif
  31. #ifdef HAVE_NETDB_H
  32. #include <netdb.h>
  33. #endif
  34. #ifdef HAVE_ARPA_INET_H
  35. #include <arpa/inet.h>
  36. #endif
  37. #ifdef __VMS
  38. #include <in.h>
  39. #include <inet.h>
  40. #endif
  41. #ifdef HAVE_SETJMP_H
  42. #include <setjmp.h>
  43. #endif
  44. #ifdef HAVE_SIGNAL_H
  45. #include <signal.h>
  46. #endif
  47. #include "urldata.h"
  48. #include "connect.h"
  49. #include "cfilters.h"
  50. #include "curl_log.h"
  51. /* copied from hostip.c to switch using SIGALARM for timeouts.
  52. * SIGALARM has only seconds resolution, so our tests will not work
  53. * here. */
  54. #if defined(CURLRES_SYNCH) && \
  55. defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
  56. #define USE_ALARM_TIMEOUT
  57. #endif
  58. static CURL *easy;
  59. static CURLcode unit_setup(void)
  60. {
  61. CURLcode res = CURLE_OK;
  62. global_init(CURL_GLOBAL_ALL);
  63. easy = curl_easy_init();
  64. if(!easy) {
  65. curl_global_cleanup();
  66. return CURLE_OUT_OF_MEMORY;
  67. }
  68. curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
  69. return res;
  70. }
  71. static void unit_stop(void)
  72. {
  73. curl_easy_cleanup(easy);
  74. curl_global_cleanup();
  75. }
  76. #ifdef DEBUGBUILD
  77. struct test_case {
  78. int id;
  79. const char *url;
  80. const char *resolve_info;
  81. unsigned char ip_version;
  82. timediff_t connect_timeout_ms;
  83. timediff_t he_timeout_ms;
  84. timediff_t cf4_fail_delay_ms;
  85. timediff_t cf6_fail_delay_ms;
  86. int exp_cf4_creations;
  87. int exp_cf6_creations;
  88. timediff_t min_duration_ms;
  89. timediff_t max_duration_ms;
  90. CURLcode exp_result;
  91. const char *pref_family;
  92. };
  93. struct ai_family_stats {
  94. const char *family;
  95. int creations;
  96. timediff_t first_created;
  97. timediff_t last_created;
  98. };
  99. struct test_result {
  100. CURLcode result;
  101. struct curltime started;
  102. struct curltime ended;
  103. struct ai_family_stats cf4;
  104. struct ai_family_stats cf6;
  105. };
  106. static struct test_case *current_tc;
  107. static struct test_result *current_tr;
  108. struct cf_test_ctx {
  109. int ai_family;
  110. int transport;
  111. char id[16];
  112. struct curltime started;
  113. timediff_t fail_delay_ms;
  114. struct ai_family_stats *stats;
  115. };
  116. static void cf_test_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
  117. {
  118. struct cf_test_ctx *ctx = cf->ctx;
  119. infof(data, "%04dms: cf[%s] destroyed",
  120. (int)Curl_timediff(Curl_now(), current_tr->started), ctx->id);
  121. free(ctx);
  122. cf->ctx = NULL;
  123. }
  124. static CURLcode cf_test_connect(struct Curl_cfilter *cf,
  125. struct Curl_easy *data,
  126. bool blocking, bool *done)
  127. {
  128. struct cf_test_ctx *ctx = cf->ctx;
  129. struct curltime now;
  130. (void)data;
  131. (void)blocking;
  132. *done = FALSE;
  133. now = Curl_now();
  134. if(Curl_timediff(now, ctx->started) >= ctx->fail_delay_ms) {
  135. infof(data, "%04dms: cf[%s] fail delay reached",
  136. (int)Curl_timediff(Curl_now(), current_tr->started), ctx->id);
  137. return CURLE_COULDNT_CONNECT;
  138. }
  139. infof(data, "%04dms: cf[%s] continuing",
  140. (int)Curl_timediff(Curl_now(), current_tr->started), ctx->id);
  141. return CURLE_OK;
  142. }
  143. static struct Curl_cftype cft_test = {
  144. "TEST",
  145. CF_TYPE_IP_CONNECT,
  146. CURL_LOG_DEFAULT,
  147. cf_test_destroy,
  148. cf_test_connect,
  149. Curl_cf_def_close,
  150. Curl_cf_def_get_host,
  151. Curl_cf_def_get_select_socks,
  152. Curl_cf_def_data_pending,
  153. Curl_cf_def_send,
  154. Curl_cf_def_recv,
  155. Curl_cf_def_cntrl,
  156. Curl_cf_def_conn_is_alive,
  157. Curl_cf_def_conn_keep_alive,
  158. Curl_cf_def_query,
  159. };
  160. static CURLcode cf_test_create(struct Curl_cfilter **pcf,
  161. struct Curl_easy *data,
  162. struct connectdata *conn,
  163. const struct Curl_addrinfo *ai,
  164. int transport)
  165. {
  166. struct cf_test_ctx *ctx = NULL;
  167. struct Curl_cfilter *cf = NULL;
  168. timediff_t created_at;
  169. CURLcode result;
  170. (void)data;
  171. (void)conn;
  172. ctx = calloc(sizeof(*ctx), 1);
  173. if(!ctx) {
  174. result = CURLE_OUT_OF_MEMORY;
  175. goto out;
  176. }
  177. ctx->ai_family = ai->ai_family;
  178. ctx->transport = transport;
  179. ctx->started = Curl_now();
  180. #ifdef ENABLE_IPV6
  181. if(ctx->ai_family == AF_INET6) {
  182. ctx->stats = &current_tr->cf6;
  183. ctx->fail_delay_ms = current_tc->cf6_fail_delay_ms;
  184. curl_msprintf(ctx->id, "v6-%d", ctx->stats->creations);
  185. ctx->stats->creations++;
  186. }
  187. else
  188. #endif
  189. {
  190. ctx->stats = &current_tr->cf4;
  191. ctx->fail_delay_ms = current_tc->cf4_fail_delay_ms;
  192. curl_msprintf(ctx->id, "v4-%d", ctx->stats->creations);
  193. ctx->stats->creations++;
  194. }
  195. created_at = Curl_timediff(ctx->started, current_tr->started);
  196. if(ctx->stats->creations == 1)
  197. ctx->stats->first_created = created_at;
  198. ctx->stats->last_created = created_at;
  199. infof(data, "%04dms: cf[%s] created", (int)created_at, ctx->id);
  200. result = Curl_cf_create(&cf, &cft_test, ctx);
  201. out:
  202. *pcf = (!result)? cf : NULL;
  203. if(result) {
  204. free(cf);
  205. free(ctx);
  206. }
  207. return result;
  208. }
  209. static void check_result(struct test_case *tc,
  210. struct test_result *tr)
  211. {
  212. char msg[256];
  213. timediff_t duration_ms = 0;
  214. if(tr->result != tc->exp_result
  215. && CURLE_OPERATION_TIMEDOUT != tr->result) {
  216. /* on CI we encounter the TIMEOUT result, since images get less CPU
  217. * and events are not as sharply timed. */
  218. curl_msprintf(msg, "%d: expected result %d but got %d",
  219. tc->id, tc->exp_result, tr->result);
  220. fail(msg);
  221. }
  222. if(tr->cf4.creations != tc->exp_cf4_creations) {
  223. curl_msprintf(msg, "%d: expected %d ipv4 creations, but got %d",
  224. tc->id, tc->exp_cf4_creations, tr->cf4.creations);
  225. fail(msg);
  226. }
  227. if(tr->cf6.creations != tc->exp_cf6_creations) {
  228. curl_msprintf(msg, "%d: expected %d ipv6 creations, but got %d",
  229. tc->id, tc->exp_cf6_creations, tr->cf6.creations);
  230. fail(msg);
  231. }
  232. (void)duration_ms;
  233. #ifndef USE_ALARM_TIMEOUT
  234. duration_ms = Curl_timediff(tr->ended, tr->started);
  235. if(duration_ms < tc->min_duration_ms) {
  236. curl_msprintf(msg, "%d: expected min duration of %dms, but took %dms",
  237. tc->id, (int)tc->min_duration_ms, (int)duration_ms);
  238. fail(msg);
  239. }
  240. if(duration_ms > tc->max_duration_ms) {
  241. curl_msprintf(msg, "%d: expected max duration of %dms, but took %dms",
  242. tc->id, (int)tc->max_duration_ms, (int)duration_ms);
  243. fail(msg);
  244. }
  245. #endif
  246. if(tr->cf6.creations && tr->cf4.creations && tc->pref_family) {
  247. /* did ipv4 and ipv6 both, expect the preferred family to start right arway
  248. * with the other being delayed by the happy_eyeball_timeout */
  249. struct ai_family_stats *stats1 = !strcmp(tc->pref_family, "v6")?
  250. &tr->cf6 : &tr->cf4;
  251. struct ai_family_stats *stats2 = !strcmp(tc->pref_family, "v6")?
  252. &tr->cf4 : &tr->cf6;
  253. if(stats1->first_created > 100) {
  254. curl_msprintf(msg, "%d: expected ip%s to start right away, instead "
  255. "first attempt made after %dms",
  256. tc->id, stats1->family, (int)stats1->first_created);
  257. fail(msg);
  258. }
  259. #ifndef USE_ALARM_TIMEOUT
  260. if(stats2->first_created < tc->he_timeout_ms) {
  261. #else
  262. if(stats2->first_created < 1000) {
  263. #endif
  264. curl_msprintf(msg, "%d: expected ip%s to start delayed after %dms, "
  265. "instead first attempt made after %dms",
  266. tc->id, stats2->family, (int)tc->he_timeout_ms,
  267. (int)stats2->first_created);
  268. fail(msg);
  269. }
  270. }
  271. }
  272. static void test_connect(struct test_case *tc)
  273. {
  274. struct test_result tr;
  275. struct curl_slist *list = NULL;
  276. Curl_debug_set_transport_provider(TRNSPRT_TCP, cf_test_create);
  277. current_tc = tc;
  278. current_tr = &tr;
  279. list = curl_slist_append(NULL, tc->resolve_info);
  280. fail_unless(list, "error allocating resolve list entry");
  281. curl_easy_setopt(easy, CURLOPT_RESOLVE, list);
  282. curl_easy_setopt(easy, CURLOPT_IPRESOLVE, (long)tc->ip_version);
  283. #ifdef USE_ALARM_TIMEOUT
  284. curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT_MS, 2000L);
  285. curl_easy_setopt(easy, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
  286. (tc->he_timeout_ms > tc->connect_timeout_ms)?
  287. 3000L : 1000L);
  288. #else
  289. curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT_MS,
  290. (long)tc->connect_timeout_ms);
  291. curl_easy_setopt(easy, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
  292. (long)tc->he_timeout_ms);
  293. #endif
  294. curl_easy_setopt(easy, CURLOPT_URL, tc->url);
  295. memset(&tr, 0, sizeof(tr));
  296. tr.cf6.family = "v6";
  297. tr.cf4.family = "v4";
  298. tr.started = Curl_now();
  299. tr.result = curl_easy_perform(easy);
  300. tr.ended = Curl_now();
  301. curl_easy_setopt(easy, CURLOPT_RESOLVE, NULL);
  302. curl_slist_free_all(list);
  303. list = NULL;
  304. current_tc = NULL;
  305. current_tr = NULL;
  306. check_result(tc, &tr);
  307. }
  308. #endif /* DEBUGBUILD */
  309. /*
  310. * How these test cases work:
  311. * - replace the creation of the TCP socket filter with our test filter
  312. * - test filter does nothing and reports failure after configured delay
  313. * - we feed addresses into the resolve cache to simulate different cases
  314. * - we monitor how many instances of ipv4/v6 attempts are made and when
  315. * - for mixed families, we expect HAPPY_EYEBALLS_TIMEOUT to trigger
  316. *
  317. * Max Duration checks needs to be conservative since CI jobs are not
  318. * as sharp.
  319. */
  320. #define TURL "http://test.com:123"
  321. #define R_FAIL CURLE_COULDNT_CONNECT
  322. static struct test_case TEST_CASES[] = {
  323. /* TIMEOUT_MS, FAIL_MS CREATED DURATION Result, HE_PREF */
  324. /* CNCT HE v4 v6 v4 v6 MIN MAX */
  325. { 1, TURL, "test.com:123:192.0.2.1", CURL_IPRESOLVE_WHATEVER,
  326. 250, 150, 200, 200, 1, 0, 200, 600, R_FAIL, NULL },
  327. /* 1 ipv4, fails after ~200ms, reports COULDNT_CONNECT */
  328. { 2, TURL, "test.com:123:192.0.2.1,192.0.2.2", CURL_IPRESOLVE_WHATEVER,
  329. 500, 150, 200, 200, 2, 0, 400, 800, R_FAIL, NULL },
  330. /* 2 ipv4, fails after ~400ms, reports COULDNT_CONNECT */
  331. #ifdef ENABLE_IPV6
  332. { 3, TURL, "test.com:123:::1", CURL_IPRESOLVE_WHATEVER,
  333. 250, 150, 200, 200, 0, 1, 200, 500, R_FAIL, NULL },
  334. /* 1 ipv6, fails after ~200ms, reports COULDNT_CONNECT */
  335. { 4, TURL, "test.com:123:::1,::2", CURL_IPRESOLVE_WHATEVER,
  336. 500, 150, 200, 200, 0, 2, 400, 800, R_FAIL, NULL },
  337. /* 2 ipv6, fails after ~400ms, reports COULDNT_CONNECT */
  338. { 5, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_WHATEVER,
  339. 500, 150, 200, 200, 1, 1, 350, 800, R_FAIL, "v4" },
  340. /* mixed ip4+6, v4 starts, v6 kicks in on HE, fails after ~350ms */
  341. { 6, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_WHATEVER,
  342. 500, 150, 200, 200, 1, 1, 350, 800, R_FAIL, "v6" },
  343. /* mixed ip6+4, v6 starts, v4 kicks in on HE, fails after ~350ms */
  344. { 7, TURL, "test.com:123:::1,192.0.2.1,::2,::3", CURL_IPRESOLVE_WHATEVER,
  345. 500, 600, 200, 200, 0, 3, 350, 800, R_FAIL, "v6" },
  346. /* mixed ip6+4, v6 starts, v4 never starts due to high HE, TIMEOUT */
  347. { 8, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_V4,
  348. 400, 150, 500, 500, 1, 0, 400, 600, R_FAIL, NULL },
  349. /* mixed ip4+6, but only use v4, check it uses full connect timeout,
  350. although another address of the 'wrong' family is available */
  351. { 9, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_V6,
  352. 400, 150, 500, 500, 0, 1, 400, 600, R_FAIL, NULL },
  353. /* mixed ip4+6, but only use v6, check it uses full connect timeout,
  354. although another address of the 'wrong' family is available */
  355. #endif
  356. };
  357. UNITTEST_START
  358. #if defined(DEBUGBUILD)
  359. size_t i;
  360. for(i = 0; i < sizeof(TEST_CASES)/sizeof(TEST_CASES[0]); ++i) {
  361. test_connect(&TEST_CASES[i]);
  362. }
  363. #else
  364. (void)TEST_CASES;
  365. (void)test_connect;
  366. #endif
  367. UNITTEST_STOP