unit2600.c 12 KB

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