source: EcnlProtoTool/trunk/curl-7.57.0/lib/asyn-thread.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc
File size: 17.3 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2017, 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
23#include "curl_setup.h"
24
25/***********************************************************************
26 * Only for threaded name resolves builds
27 **********************************************************************/
28#ifdef CURLRES_THREADED
29
30#ifdef HAVE_NETINET_IN_H
31#include <netinet/in.h>
32#endif
33#ifdef HAVE_NETDB_H
34#include <netdb.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef __VMS
40#include <in.h>
41#include <inet.h>
42#endif
43
44#if defined(USE_THREADS_POSIX)
45# ifdef HAVE_PTHREAD_H
46# include <pthread.h>
47# endif
48#elif defined(USE_THREADS_WIN32)
49# ifdef HAVE_PROCESS_H
50# include <process.h>
51# endif
52#endif
53
54#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55#undef in_addr_t
56#define in_addr_t unsigned long
57#endif
58
59#ifdef HAVE_GETADDRINFO
60# define RESOLVER_ENOMEM EAI_MEMORY
61#else
62# define RESOLVER_ENOMEM ENOMEM
63#endif
64
65#include "urldata.h"
66#include "sendf.h"
67#include "hostip.h"
68#include "hash.h"
69#include "share.h"
70#include "strerror.h"
71#include "url.h"
72#include "multiif.h"
73#include "inet_pton.h"
74#include "inet_ntop.h"
75#include "curl_threads.h"
76#include "connect.h"
77/* The last 3 #include files should be in this order */
78#include "curl_printf.h"
79#include "curl_memory.h"
80#include "memdebug.h"
81
82/*
83 * Curl_resolver_global_init()
84 * Called from curl_global_init() to initialize global resolver environment.
85 * Does nothing here.
86 */
87int Curl_resolver_global_init(void)
88{
89 return CURLE_OK;
90}
91
92/*
93 * Curl_resolver_global_cleanup()
94 * Called from curl_global_cleanup() to destroy global resolver environment.
95 * Does nothing here.
96 */
97void Curl_resolver_global_cleanup(void)
98{
99}
100
101/*
102 * Curl_resolver_init()
103 * Called from curl_easy_init() -> Curl_open() to initialize resolver
104 * URL-state specific environment ('resolver' member of the UrlState
105 * structure). Does nothing here.
106 */
107CURLcode Curl_resolver_init(void **resolver)
108{
109 (void)resolver;
110 return CURLE_OK;
111}
112
113/*
114 * Curl_resolver_cleanup()
115 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
116 * URL-state specific environment ('resolver' member of the UrlState
117 * structure). Does nothing here.
118 */
119void Curl_resolver_cleanup(void *resolver)
120{
121 (void)resolver;
122}
123
124/*
125 * Curl_resolver_duphandle()
126 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
127 * environment ('resolver' member of the UrlState structure). Does nothing
128 * here.
129 */
130int Curl_resolver_duphandle(void **to, void *from)
131{
132 (void)to;
133 (void)from;
134 return CURLE_OK;
135}
136
137static void destroy_async_data(struct Curl_async *);
138
139/*
140 * Cancel all possibly still on-going resolves for this connection.
141 */
142void Curl_resolver_cancel(struct connectdata *conn)
143{
144 destroy_async_data(&conn->async);
145}
146
147/* This function is used to init a threaded resolve */
148static bool init_resolve_thread(struct connectdata *conn,
149 const char *hostname, int port,
150 const struct addrinfo *hints);
151
152
153/* Data for synchronization between resolver thread and its parent */
154struct thread_sync_data {
155 curl_mutex_t * mtx;
156 int done;
157
158 char *hostname; /* hostname to resolve, Curl_async.hostname
159 duplicate */
160 int port;
161 int sock_error;
162 Curl_addrinfo *res;
163#ifdef HAVE_GETADDRINFO
164 struct addrinfo hints;
165#endif
166 struct thread_data *td; /* for thread-self cleanup */
167};
168
169struct thread_data {
170 curl_thread_t thread_hnd;
171 unsigned int poll_interval;
172 time_t interval_end;
173 struct thread_sync_data tsd;
174};
175
176static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
177{
178 return &(((struct thread_data *)conn->async.os_specific)->tsd);
179}
180
181#define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
182
183/* Destroy resolver thread synchronization data */
184static
185void destroy_thread_sync_data(struct thread_sync_data * tsd)
186{
187 if(tsd->mtx) {
188 Curl_mutex_destroy(tsd->mtx);
189 free(tsd->mtx);
190 }
191
192 free(tsd->hostname);
193
194 if(tsd->res)
195 Curl_freeaddrinfo(tsd->res);
196
197 memset(tsd, 0, sizeof(*tsd));
198}
199
200/* Initialize resolver thread synchronization data */
201static
202int init_thread_sync_data(struct thread_data * td,
203 const char *hostname,
204 int port,
205 const struct addrinfo *hints)
206{
207 struct thread_sync_data *tsd = &td->tsd;
208
209 memset(tsd, 0, sizeof(*tsd));
210
211 tsd->td = td;
212 tsd->port = port;
213 /* Treat the request as done until the thread actually starts so any early
214 * cleanup gets done properly.
215 */
216 tsd->done = 1;
217#ifdef HAVE_GETADDRINFO
218 DEBUGASSERT(hints);
219 tsd->hints = *hints;
220#else
221 (void) hints;
222#endif
223
224 tsd->mtx = malloc(sizeof(curl_mutex_t));
225 if(tsd->mtx == NULL)
226 goto err_exit;
227
228 Curl_mutex_init(tsd->mtx);
229
230 tsd->sock_error = CURL_ASYNC_SUCCESS;
231
232 /* Copying hostname string because original can be destroyed by parent
233 * thread during gethostbyname execution.
234 */
235 tsd->hostname = strdup(hostname);
236 if(!tsd->hostname)
237 goto err_exit;
238
239 return 1;
240
241 err_exit:
242 /* Memory allocation failed */
243 destroy_thread_sync_data(tsd);
244 return 0;
245}
246
247static int getaddrinfo_complete(struct connectdata *conn)
248{
249 struct thread_sync_data *tsd = conn_thread_sync_data(conn);
250 int rc;
251
252 rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
253 /* The tsd->res structure has been copied to async.dns and perhaps the DNS
254 cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it.
255 */
256 tsd->res = NULL;
257
258 return rc;
259}
260
261
262#ifdef HAVE_GETADDRINFO
263
264/*
265 * getaddrinfo_thread() resolves a name and then exits.
266 *
267 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
268 * and wait on it.
269 */
270static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
271{
272 struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
273 struct thread_data *td = tsd->td;
274 char service[12];
275 int rc;
276
277 snprintf(service, sizeof(service), "%d", tsd->port);
278
279 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
280
281 if(rc != 0) {
282 tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
283 if(tsd->sock_error == 0)
284 tsd->sock_error = RESOLVER_ENOMEM;
285 }
286 else {
287 Curl_addrinfo_set_port(tsd->res, tsd->port);
288 }
289
290 Curl_mutex_acquire(tsd->mtx);
291 if(tsd->done) {
292 /* too late, gotta clean up the mess */
293 Curl_mutex_release(tsd->mtx);
294 destroy_thread_sync_data(tsd);
295 free(td);
296 }
297 else {
298 tsd->done = 1;
299 Curl_mutex_release(tsd->mtx);
300 }
301
302 return 0;
303}
304
305#else /* HAVE_GETADDRINFO */
306
307/*
308 * gethostbyname_thread() resolves a name and then exits.
309 */
310static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
311{
312 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
313 struct thread_data *td = tsd->td;
314
315 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
316
317 if(!tsd->res) {
318 tsd->sock_error = SOCKERRNO;
319 if(tsd->sock_error == 0)
320 tsd->sock_error = RESOLVER_ENOMEM;
321 }
322
323 Curl_mutex_acquire(tsd->mtx);
324 if(tsd->done) {
325 /* too late, gotta clean up the mess */
326 Curl_mutex_release(tsd->mtx);
327 destroy_thread_sync_data(tsd);
328 free(td);
329 }
330 else {
331 tsd->done = 1;
332 Curl_mutex_release(tsd->mtx);
333 }
334
335 return 0;
336}
337
338#endif /* HAVE_GETADDRINFO */
339
340/*
341 * destroy_async_data() cleans up async resolver data and thread handle.
342 */
343static void destroy_async_data(struct Curl_async *async)
344{
345 if(async->os_specific) {
346 struct thread_data *td = (struct thread_data*) async->os_specific;
347 int done;
348
349 /*
350 * if the thread is still blocking in the resolve syscall, detach it and
351 * let the thread do the cleanup...
352 */
353 Curl_mutex_acquire(td->tsd.mtx);
354 done = td->tsd.done;
355 td->tsd.done = 1;
356 Curl_mutex_release(td->tsd.mtx);
357
358 if(!done) {
359 Curl_thread_destroy(td->thread_hnd);
360 }
361 else {
362 if(td->thread_hnd != curl_thread_t_null)
363 Curl_thread_join(&td->thread_hnd);
364
365 destroy_thread_sync_data(&td->tsd);
366
367 free(async->os_specific);
368 }
369 }
370 async->os_specific = NULL;
371
372 free(async->hostname);
373 async->hostname = NULL;
374}
375
376/*
377 * init_resolve_thread() starts a new thread that performs the actual
378 * resolve. This function returns before the resolve is done.
379 *
380 * Returns FALSE in case of failure, otherwise TRUE.
381 */
382static bool init_resolve_thread(struct connectdata *conn,
383 const char *hostname, int port,
384 const struct addrinfo *hints)
385{
386 struct thread_data *td = calloc(1, sizeof(struct thread_data));
387 int err = ENOMEM;
388
389 conn->async.os_specific = (void *)td;
390 if(!td)
391 goto errno_exit;
392
393 conn->async.port = port;
394 conn->async.done = FALSE;
395 conn->async.status = 0;
396 conn->async.dns = NULL;
397 td->thread_hnd = curl_thread_t_null;
398
399 if(!init_thread_sync_data(td, hostname, port, hints)) {
400 conn->async.os_specific = NULL;
401 free(td);
402 goto errno_exit;
403 }
404
405 free(conn->async.hostname);
406 conn->async.hostname = strdup(hostname);
407 if(!conn->async.hostname)
408 goto err_exit;
409
410 /* The thread will set this to 1 when complete. */
411 td->tsd.done = 0;
412
413#ifdef HAVE_GETADDRINFO
414 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
415#else
416 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
417#endif
418
419 if(!td->thread_hnd) {
420 /* The thread never started, so mark it as done here for proper cleanup. */
421 td->tsd.done = 1;
422 err = errno;
423 goto err_exit;
424 }
425
426 return TRUE;
427
428 err_exit:
429 destroy_async_data(&conn->async);
430
431 errno_exit:
432 errno = err;
433 return FALSE;
434}
435
436/*
437 * resolver_error() calls failf() with the appropriate message after a resolve
438 * error
439 */
440
441static CURLcode resolver_error(struct connectdata *conn)
442{
443 const char *host_or_proxy;
444 CURLcode result;
445
446 if(conn->bits.httpproxy) {
447 host_or_proxy = "proxy";
448 result = CURLE_COULDNT_RESOLVE_PROXY;
449 }
450 else {
451 host_or_proxy = "host";
452 result = CURLE_COULDNT_RESOLVE_HOST;
453 }
454
455 failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
456 conn->async.hostname);
457
458 return result;
459}
460
461/*
462 * Curl_resolver_wait_resolv()
463 *
464 * waits for a resolve to finish. This function should be avoided since using
465 * this risk getting the multi interface to "hang".
466 *
467 * If 'entry' is non-NULL, make it point to the resolved dns entry
468 *
469 * This is the version for resolves-in-a-thread.
470 */
471CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
472 struct Curl_dns_entry **entry)
473{
474 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
475 CURLcode result = CURLE_OK;
476
477 DEBUGASSERT(conn && td);
478
479 /* wait for the thread to resolve the name */
480 if(Curl_thread_join(&td->thread_hnd))
481 result = getaddrinfo_complete(conn);
482 else
483 DEBUGASSERT(0);
484
485 conn->async.done = TRUE;
486
487 if(entry)
488 *entry = conn->async.dns;
489
490 if(!conn->async.dns)
491 /* a name was not resolved, report error */
492 result = resolver_error(conn);
493
494 destroy_async_data(&conn->async);
495
496 if(!conn->async.dns)
497 connclose(conn, "asynch resolve failed");
498
499 return result;
500}
501
502/*
503 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
504 * name resolve request has completed. It should also make sure to time-out if
505 * the operation seems to take too long.
506 */
507CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
508 struct Curl_dns_entry **entry)
509{
510 struct Curl_easy *data = conn->data;
511 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
512 int done = 0;
513
514 *entry = NULL;
515
516 if(!td) {
517 DEBUGASSERT(td);
518 return CURLE_COULDNT_RESOLVE_HOST;
519 }
520
521 Curl_mutex_acquire(td->tsd.mtx);
522 done = td->tsd.done;
523 Curl_mutex_release(td->tsd.mtx);
524
525 if(done) {
526 getaddrinfo_complete(conn);
527
528 if(!conn->async.dns) {
529 CURLcode result = resolver_error(conn);
530 destroy_async_data(&conn->async);
531 return result;
532 }
533 destroy_async_data(&conn->async);
534 *entry = conn->async.dns;
535 }
536 else {
537 /* poll for name lookup done with exponential backoff up to 250ms */
538 timediff_t elapsed = Curl_timediff(Curl_now(),
539 data->progress.t_startsingle);
540 if(elapsed < 0)
541 elapsed = 0;
542
543 if(td->poll_interval == 0)
544 /* Start at 1ms poll interval */
545 td->poll_interval = 1;
546 else if(elapsed >= td->interval_end)
547 /* Back-off exponentially if last interval expired */
548 td->poll_interval *= 2;
549
550 if(td->poll_interval > 250)
551 td->poll_interval = 250;
552
553 td->interval_end = elapsed + td->poll_interval;
554 Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME);
555 }
556
557 return CURLE_OK;
558}
559
560int Curl_resolver_getsock(struct connectdata *conn,
561 curl_socket_t *socks,
562 int numsocks)
563{
564 (void)conn;
565 (void)socks;
566 (void)numsocks;
567 return 0;
568}
569
570#ifndef HAVE_GETADDRINFO
571/*
572 * Curl_getaddrinfo() - for platforms without getaddrinfo
573 */
574Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
575 const char *hostname,
576 int port,
577 int *waitp)
578{
579 struct in_addr in;
580
581 *waitp = 0; /* default to synchronous response */
582
583 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
584 /* This is a dotted IP address 123.123.123.123-style */
585 return Curl_ip2addr(AF_INET, &in, hostname, port);
586
587 /* fire up a new resolver thread! */
588 if(init_resolve_thread(conn, hostname, port, NULL)) {
589 *waitp = 1; /* expect asynchronous response */
590 return NULL;
591 }
592
593 /* fall-back to blocking version */
594 return Curl_ipv4_resolve_r(hostname, port);
595}
596
597#else /* !HAVE_GETADDRINFO */
598
599/*
600 * Curl_resolver_getaddrinfo() - for getaddrinfo
601 */
602Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
603 const char *hostname,
604 int port,
605 int *waitp)
606{
607 struct addrinfo hints;
608 Curl_addrinfo *res;
609 int error;
610 char sbuf[12];
611 int pf = PF_INET;
612
613 *waitp = 0; /* default to synchronous response */
614
615#ifndef USE_RESOLVE_ON_IPS
616 {
617 struct in_addr in;
618 /* First check if this is an IPv4 address string */
619 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
620 /* This is a dotted IP address 123.123.123.123-style */
621 return Curl_ip2addr(AF_INET, &in, hostname, port);
622 }
623#ifdef CURLRES_IPV6
624 {
625 struct in6_addr in6;
626 /* check if this is an IPv6 address string */
627 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
628 /* This is an IPv6 address literal */
629 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
630 }
631#endif /* CURLRES_IPV6 */
632#endif /* !USE_RESOLVE_ON_IPS */
633
634#ifdef CURLRES_IPV6
635 /*
636 * Check if a limited name resolve has been requested.
637 */
638 switch(conn->ip_version) {
639 case CURL_IPRESOLVE_V4:
640 pf = PF_INET;
641 break;
642 case CURL_IPRESOLVE_V6:
643 pf = PF_INET6;
644 break;
645 default:
646 pf = PF_UNSPEC;
647 break;
648 }
649
650 if((pf != PF_INET) && !Curl_ipv6works())
651 /* The stack seems to be a non-IPv6 one */
652 pf = PF_INET;
653#endif /* CURLRES_IPV6 */
654
655 memset(&hints, 0, sizeof(hints));
656 hints.ai_family = pf;
657 hints.ai_socktype = conn->socktype;
658
659 snprintf(sbuf, sizeof(sbuf), "%d", port);
660
661 /* fire up a new resolver thread! */
662 if(init_resolve_thread(conn, hostname, port, &hints)) {
663 *waitp = 1; /* expect asynchronous response */
664 return NULL;
665 }
666
667 /* fall-back to blocking version */
668 infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
669 hostname, Curl_strerror(conn, errno));
670
671 error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
672 if(error) {
673 infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
674 hostname, port, Curl_strerror(conn, SOCKERRNO));
675 return NULL;
676 }
677 else {
678 Curl_addrinfo_set_port(res, port);
679 }
680
681 return res;
682}
683
684#endif /* !HAVE_GETADDRINFO */
685
686CURLcode Curl_set_dns_servers(struct Curl_easy *data,
687 char *servers)
688{
689 (void)data;
690 (void)servers;
691 return CURLE_NOT_BUILT_IN;
692
693}
694
695CURLcode Curl_set_dns_interface(struct Curl_easy *data,
696 const char *interf)
697{
698 (void)data;
699 (void)interf;
700 return CURLE_NOT_BUILT_IN;
701}
702
703CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
704 const char *local_ip4)
705{
706 (void)data;
707 (void)local_ip4;
708 return CURLE_NOT_BUILT_IN;
709}
710
711CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
712 const char *local_ip6)
713{
714 (void)data;
715 (void)local_ip6;
716 return CURLE_NOT_BUILT_IN;
717}
718
719#endif /* CURLRES_THREADED */
Note: See TracBrowser for help on using the repository browser.