Mercurial > pidgin.yaz
comparison libpurple/protocols/gg/lib/resolver.c @ 32827:4a34689eeb33 default tip
merged from im.pidgin.pidgin
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Sat, 19 Nov 2011 14:42:54 +0900 |
parents | e2ff2ac0e022 |
children |
comparison
equal
deleted
inserted
replaced
32692:0f94ec89f0bc | 32827:4a34689eeb33 |
---|---|
24 * \file resolver.c | 24 * \file resolver.c |
25 * | 25 * |
26 * \brief Funkcje rozwiązywania nazw | 26 * \brief Funkcje rozwiązywania nazw |
27 */ | 27 */ |
28 | 28 |
29 #ifndef _WIN32 | |
30 # include <sys/wait.h> | |
31 # include <netdb.h> | |
32 #endif | |
33 #include <errno.h> | 29 #include <errno.h> |
34 #include <stdlib.h> | 30 #include <stdlib.h> |
35 #include <string.h> | 31 #include <string.h> |
36 #include <unistd.h> | 32 #include <unistd.h> |
37 #ifndef _WIN32 | |
38 # include <signal.h> | |
39 # include <netinet/in.h> | |
40 # include <arpa/inet.h> | |
41 #endif | |
42 | 33 |
43 #include "libgadu.h" | 34 #include "libgadu.h" |
44 #include "libgadu-config.h" | |
45 #include "resolver.h" | 35 #include "resolver.h" |
46 #include "compat.h" | 36 #include "compat.h" |
47 #include "session.h" | 37 #include "session.h" |
38 | |
39 #ifdef GG_CONFIG_HAVE_FORK | |
40 #include <sys/wait.h> | |
41 #include <signal.h> | |
42 #endif | |
48 | 43 |
49 /** Sposób rozwiązywania nazw serwerów */ | 44 /** Sposób rozwiązywania nazw serwerów */ |
50 static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; | 45 static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; |
51 | 46 |
52 /** Funkcja rozpoczynająca rozwiązywanie nazwy */ | 47 /** Funkcja rozpoczynająca rozwiązywanie nazwy */ |
247 | 242 |
248 return 0; | 243 return 0; |
249 #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ | 244 #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ |
250 } | 245 } |
251 | 246 |
252 #if defined(GG_CONFIG_HAVE_PTHREAD) || !defined(_WIN32) | 247 #if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_HAVE_FORK) |
248 | |
253 /** | 249 /** |
254 * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. | 250 * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. |
255 * | 251 * |
256 * \param fd Deskryptor | 252 * \param fd Deskryptor |
257 * \param hostname Nazwa serwera | 253 * \param hostname Nazwa serwera |
285 if (addr_list != addr_ip) | 281 if (addr_list != addr_ip) |
286 free(addr_list); | 282 free(addr_list); |
287 | 283 |
288 return res; | 284 return res; |
289 } | 285 } |
290 #endif | 286 |
287 #endif /* GG_CONFIG_HAVE_PTHREAD || GG_CONFIG_HAVE_FORK */ | |
291 | 288 |
292 /** | 289 /** |
293 * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. | 290 * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. |
294 * | 291 * |
295 * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli | 292 * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli |
310 return NULL; | 307 return NULL; |
311 | 308 |
312 return result; | 309 return result; |
313 } | 310 } |
314 | 311 |
312 #ifdef GG_CONFIG_HAVE_FORK | |
313 | |
315 /** | 314 /** |
316 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. | 315 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. |
317 */ | 316 */ |
318 struct gg_resolver_fork_data { | 317 struct gg_resolver_fork_data { |
319 int pid; /*< Identyfikator procesu */ | 318 int pid; /*< Identyfikator procesu */ |
320 }; | 319 }; |
321 | 320 |
322 #ifdef _WIN32 | |
323 /** | |
324 * Deal with the fact that you can't select() on a win32 file fd. | |
325 * This makes it practically impossible to tie into purple's event loop. | |
326 * | |
327 * -This is thanks to Tor Lillqvist. | |
328 * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. | |
329 */ | |
330 static int | |
331 socket_pipe (int *fds) | |
332 { | |
333 SOCKET temp, socket1 = -1, socket2 = -1; | |
334 struct sockaddr_in saddr; | |
335 int len; | |
336 u_long arg; | |
337 fd_set read_set, write_set; | |
338 struct timeval tv; | |
339 | |
340 temp = socket(AF_INET, SOCK_STREAM, 0); | |
341 | |
342 if (temp == INVALID_SOCKET) { | |
343 goto out0; | |
344 } | |
345 | |
346 arg = 1; | |
347 if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { | |
348 goto out0; | |
349 } | |
350 | |
351 memset(&saddr, 0, sizeof(saddr)); | |
352 saddr.sin_family = AF_INET; | |
353 saddr.sin_port = 0; | |
354 saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
355 | |
356 if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { | |
357 goto out0; | |
358 } | |
359 | |
360 if (listen(temp, 1) == SOCKET_ERROR) { | |
361 goto out0; | |
362 } | |
363 | |
364 len = sizeof(saddr); | |
365 if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { | |
366 goto out0; | |
367 } | |
368 | |
369 socket1 = socket(AF_INET, SOCK_STREAM, 0); | |
370 | |
371 if (socket1 == INVALID_SOCKET) { | |
372 goto out0; | |
373 } | |
374 | |
375 arg = 1; | |
376 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { | |
377 goto out1; | |
378 } | |
379 | |
380 if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || | |
381 WSAGetLastError() != WSAEWOULDBLOCK) { | |
382 goto out1; | |
383 } | |
384 | |
385 FD_ZERO(&read_set); | |
386 FD_SET(temp, &read_set); | |
387 | |
388 tv.tv_sec = 0; | |
389 tv.tv_usec = 0; | |
390 | |
391 if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { | |
392 goto out1; | |
393 } | |
394 | |
395 if (!FD_ISSET(temp, &read_set)) { | |
396 goto out1; | |
397 } | |
398 | |
399 socket2 = accept(temp, (struct sockaddr *) &saddr, &len); | |
400 if (socket2 == INVALID_SOCKET) { | |
401 goto out1; | |
402 } | |
403 | |
404 FD_ZERO(&write_set); | |
405 FD_SET(socket1, &write_set); | |
406 | |
407 tv.tv_sec = 0; | |
408 tv.tv_usec = 0; | |
409 | |
410 if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { | |
411 goto out2; | |
412 } | |
413 | |
414 if (!FD_ISSET(socket1, &write_set)) { | |
415 goto out2; | |
416 } | |
417 | |
418 arg = 0; | |
419 if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { | |
420 goto out2; | |
421 } | |
422 | |
423 arg = 0; | |
424 if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { | |
425 goto out2; | |
426 } | |
427 | |
428 fds[0] = socket1; | |
429 fds[1] = socket2; | |
430 | |
431 closesocket (temp); | |
432 | |
433 return 0; | |
434 | |
435 out2: | |
436 closesocket (socket2); | |
437 out1: | |
438 closesocket (socket1); | |
439 out0: | |
440 closesocket (temp); | |
441 errno = EIO; /* XXX */ | |
442 | |
443 return -1; | |
444 } | |
445 #endif | |
446 | |
447 | |
448 | |
449 #ifdef _WIN32 | |
450 struct gg_resolve_win32thread_data { | |
451 char *hostname; | |
452 int fd; | |
453 }; | |
454 | |
455 static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) | |
456 { | |
457 struct gg_resolve_win32thread_data *d = arg; | |
458 struct in_addr addr_ip[2], *addr_list; | |
459 int addr_count; | |
460 | |
461 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); | |
462 | |
463 if ((addr_ip[0].s_addr = inet_addr(d->hostname)) == INADDR_NONE) { | |
464 /* W przypadku błędu gg_gethostbyname_real() zwróci -1 | |
465 * i nie zmieni &addr. Tam jest już INADDR_NONE, | |
466 * więc nie musimy robić nic więcej. */ | |
467 if (gg_gethostbyname_real(d->hostname, &addr_list, &addr_count, 0) == -1) | |
468 { | |
469 addr_list = addr_ip; | |
470 } | |
471 } else { | |
472 addr_list = addr_ip; | |
473 addr_ip[1].s_addr = INADDR_NONE; | |
474 addr_count = 1; | |
475 } | |
476 | |
477 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() count = %d\n", addr_count); | |
478 | |
479 write(d->fd, addr_list, (addr_count+1) * sizeof(struct in_addr)); | |
480 close(d->fd); | |
481 | |
482 free(d->hostname); | |
483 d->hostname = NULL; | |
484 | |
485 free(d); | |
486 | |
487 if (addr_list != addr_ip) | |
488 free(addr_list); | |
489 | |
490 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); | |
491 | |
492 return 0; | |
493 } | |
494 | |
495 | |
496 static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) | |
497 { | |
498 struct gg_resolve_win32thread_data *d = NULL; | |
499 HANDLE h; | |
500 DWORD dwTId; | |
501 int pipes[2], new_errno; | |
502 | |
503 gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); | |
504 | |
505 if (!resolver || !fd || !hostname) { | |
506 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); | |
507 errno = EFAULT; | |
508 return -1; | |
509 } | |
510 | |
511 if (socket_pipe(pipes) == -1) { | |
512 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); | |
513 return -1; | |
514 } | |
515 | |
516 if (!(d = malloc(sizeof(*d)))) { | |
517 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); | |
518 new_errno = errno; | |
519 goto cleanup; | |
520 } | |
521 | |
522 d->hostname = NULL; | |
523 | |
524 if (!(d->hostname = strdup(hostname))) { | |
525 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); | |
526 new_errno = errno; | |
527 goto cleanup; | |
528 } | |
529 | |
530 d->fd = pipes[1]; | |
531 | |
532 h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, | |
533 d, 0, &dwTId); | |
534 | |
535 if (h == NULL) { | |
536 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); | |
537 new_errno = errno; | |
538 goto cleanup; | |
539 } | |
540 | |
541 *resolver = h; | |
542 *fd = pipes[0]; | |
543 | |
544 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); | |
545 | |
546 return 0; | |
547 | |
548 cleanup: | |
549 if (d) { | |
550 free(d->hostname); | |
551 free(d); | |
552 } | |
553 | |
554 close(pipes[0]); | |
555 close(pipes[1]); | |
556 | |
557 errno = new_errno; | |
558 | |
559 return -1; | |
560 | |
561 } | |
562 | |
563 static void gg_resolve_win32thread_cleanup(void **priv_data, int force) | |
564 { | |
565 struct gg_resolve_win32thread_data *data; | |
566 | |
567 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); | |
568 | |
569 if (priv_data == NULL || *priv_data == NULL) | |
570 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); | |
571 return; | |
572 | |
573 data = (struct gg_resolve_win32thread_data*) *priv_data; | |
574 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); | |
575 *priv_data = NULL; | |
576 | |
577 if (force) { | |
578 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); | |
579 //pthread_cancel(data->thread); | |
580 //pthread_join(data->thread, NULL); | |
581 } | |
582 | |
583 free(data->hostname); | |
584 data->hostname = NULL; | |
585 | |
586 if (data->fd != -1) { | |
587 close(data->fd); | |
588 data->fd = -1; | |
589 } | |
590 gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); | |
591 free(data); | |
592 } | |
593 #endif | |
594 | |
595 #ifndef _WIN32 | |
596 /** | 321 /** |
597 * \internal Rozwiązuje nazwę serwera w osobnym procesie. | 322 * \internal Rozwiązuje nazwę serwera w osobnym procesie. |
598 * | 323 * |
599 * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania | 324 * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania |
600 * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim | 325 * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim |
642 new_errno = errno; | 367 new_errno = errno; |
643 goto cleanup; | 368 goto cleanup; |
644 } | 369 } |
645 | 370 |
646 if (data->pid == 0) { | 371 if (data->pid == 0) { |
372 int status; | |
373 | |
647 close(pipes[0]); | 374 close(pipes[0]); |
648 | 375 |
649 if (gg_resolver_run(pipes[1], hostname) == -1) | 376 status = (gg_resolver_run(pipes[1], hostname) == -1) ? 1 : 0; |
650 _exit(1); | 377 |
651 else | 378 #ifdef GG_CONFIG_HAVE__EXIT |
652 _exit(0); | 379 _exit(status); |
380 #else | |
381 exit(status); | |
382 #endif | |
653 } | 383 } |
654 | 384 |
655 close(pipes[1]); | 385 close(pipes[1]); |
656 | 386 |
657 gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); | 387 gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); |
696 | 426 |
697 waitpid(data->pid, NULL, WNOHANG); | 427 waitpid(data->pid, NULL, WNOHANG); |
698 | 428 |
699 free(data); | 429 free(data); |
700 } | 430 } |
701 #endif | 431 |
432 #endif /* GG_CONFIG_HAVE_FORK */ | |
702 | 433 |
703 #ifdef GG_CONFIG_HAVE_PTHREAD | 434 #ifdef GG_CONFIG_HAVE_PTHREAD |
704 | 435 |
705 /** | 436 /** |
706 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. | 437 * \internal Struktura przekazywana do wątku rozwiązującego nazwę. |
866 gs->resolver_start = gg_global_resolver_start; | 597 gs->resolver_start = gg_global_resolver_start; |
867 gs->resolver_cleanup = gg_global_resolver_cleanup; | 598 gs->resolver_cleanup = gg_global_resolver_cleanup; |
868 return 0; | 599 return 0; |
869 } | 600 } |
870 | 601 |
871 #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) | 602 #if defined(GG_CONFIG_HAVE_PTHREAD) && defined(GG_CONFIG_PTHREAD_DEFAULT) |
872 # ifdef _WIN32 | 603 type = GG_RESOLVER_PTHREAD; |
873 type = GG_RESOLVER_WIN32; | 604 #elif defined(GG_CONFIG_HAVE_FORK) |
874 # else | |
875 type = GG_RESOLVER_FORK; | 605 type = GG_RESOLVER_FORK; |
876 # endif | |
877 #else | |
878 type = GG_RESOLVER_PTHREAD; | |
879 #endif | 606 #endif |
880 } | 607 } |
881 | 608 |
882 switch (type) { | 609 switch (type) { |
883 #ifdef _WIN32 | 610 #ifdef GG_CONFIG_HAVE_FORK |
884 case GG_RESOLVER_WIN32: | |
885 gs->resolver_type = type; | |
886 gs->resolver_start = gg_resolve_win32thread; | |
887 gs->resolver_cleanup = gg_resolve_win32thread_cleanup; | |
888 return 0; | |
889 #else | |
890 case GG_RESOLVER_FORK: | 611 case GG_RESOLVER_FORK: |
891 gs->resolver_type = type; | 612 gs->resolver_type = type; |
892 gs->resolver_start = gg_resolver_fork_start; | 613 gs->resolver_start = gg_resolver_fork_start; |
893 gs->resolver_cleanup = gg_resolver_fork_cleanup; | 614 gs->resolver_cleanup = gg_resolver_fork_cleanup; |
894 return 0; | 615 return 0; |
987 gh->resolver_start = gg_global_resolver_start; | 708 gh->resolver_start = gg_global_resolver_start; |
988 gh->resolver_cleanup = gg_global_resolver_cleanup; | 709 gh->resolver_cleanup = gg_global_resolver_cleanup; |
989 return 0; | 710 return 0; |
990 } | 711 } |
991 | 712 |
992 #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) | 713 #if defined(GG_CONFIG_HAVE_PTHREAD) && defined(GG_CONFIG_PTHREAD_DEFAULT) |
993 # ifdef _WIN32 | 714 type = GG_RESOLVER_PTHREAD; |
994 type = GG_RESOLVER_WIN32; | 715 #elif defined(GG_CONFIG_HAVE_FORK) |
995 # else | |
996 type = GG_RESOLVER_FORK; | 716 type = GG_RESOLVER_FORK; |
997 # endif | |
998 #else | |
999 type = GG_RESOLVER_PTHREAD; | |
1000 #endif | 717 #endif |
1001 } | 718 } |
1002 | 719 |
1003 switch (type) { | 720 switch (type) { |
1004 #ifdef _WIN32 | 721 #ifdef GG_CONFIG_HAVE_FORK |
1005 case GG_RESOLVER_WIN32: | |
1006 gh->resolver_type = type; | |
1007 gh->resolver_start = gg_resolve_win32thread; | |
1008 gh->resolver_cleanup = gg_resolve_win32thread_cleanup; | |
1009 return 0; | |
1010 #else | |
1011 case GG_RESOLVER_FORK: | 722 case GG_RESOLVER_FORK: |
1012 gh->resolver_type = type; | 723 gh->resolver_type = type; |
1013 gh->resolver_start = gg_resolver_fork_start; | 724 gh->resolver_start = gg_resolver_fork_start; |
1014 gh->resolver_cleanup = gg_resolver_fork_cleanup; | 725 gh->resolver_cleanup = gg_resolver_fork_cleanup; |
1015 return 0; | 726 return 0; |
1083 gg_global_resolver_type = type; | 794 gg_global_resolver_type = type; |
1084 gg_global_resolver_start = NULL; | 795 gg_global_resolver_start = NULL; |
1085 gg_global_resolver_cleanup = NULL; | 796 gg_global_resolver_cleanup = NULL; |
1086 return 0; | 797 return 0; |
1087 | 798 |
1088 #ifdef _WIN32 | 799 #ifdef GG_CONFIG_HAVE_FORK |
1089 case GG_RESOLVER_WIN32: | |
1090 gg_global_resolver_type = type; | |
1091 gg_global_resolver_start = gg_resolve_win32thread; | |
1092 gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup; | |
1093 return 0; | |
1094 #else | |
1095 case GG_RESOLVER_FORK: | 800 case GG_RESOLVER_FORK: |
1096 gg_global_resolver_type = type; | 801 gg_global_resolver_type = type; |
1097 gg_global_resolver_start = gg_resolver_fork_start; | 802 gg_global_resolver_start = gg_resolver_fork_start; |
1098 gg_global_resolver_cleanup = gg_resolver_fork_cleanup; | 803 gg_global_resolver_cleanup = gg_resolver_fork_cleanup; |
1099 return 0; | 804 return 0; |