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;