Mercurial > pidgin
comparison src/proxy.c @ 14164:879bb47cff8e
[gaim-migrate @ 16814]
Revert SVN revision 16811. Kevin says it's crashing for him and
I'm tired.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Thu, 17 Aug 2006 10:04:21 +0000 |
| parents | c3167a1dd817 |
| children | fc92a5a7dd95 |
comparison
equal
deleted
inserted
replaced
| 14163:c3167a1dd817 | 14164:879bb47cff8e |
|---|---|
| 29 , 2nd provide an easy way to add socks support | 29 , 2nd provide an easy way to add socks support |
| 30 , 3rd draw women to it like flies to honey */ | 30 , 3rd draw women to it like flies to honey */ |
| 31 | 31 |
| 32 #include "internal.h" | 32 #include "internal.h" |
| 33 #include "cipher.h" | 33 #include "cipher.h" |
| 34 #include "dnsquery.h" | |
| 35 #include "debug.h" | 34 #include "debug.h" |
| 36 #include "notify.h" | 35 #include "notify.h" |
| 37 #include "ntlm.h" | 36 #include "ntlm.h" |
| 38 #include "prefs.h" | 37 #include "prefs.h" |
| 39 #include "proxy.h" | 38 #include "proxy.h" |
| 45 char *host; | 44 char *host; |
| 46 int port; | 45 int port; |
| 47 int fd; | 46 int fd; |
| 48 guint inpa; | 47 guint inpa; |
| 49 GaimProxyInfo *gpi; | 48 GaimProxyInfo *gpi; |
| 50 GaimDnsqueryData *query_data; | |
| 51 | 49 |
| 52 /** | 50 /** |
| 53 * This contains alternating length/char* values. The char* | 51 * This contains alternating length/char* values. The char* |
| 54 * values need to be freed when removed from the linked list. | 52 * values need to be freed when removed from the linked list. |
| 55 */ | 53 */ |
| 307 { | 305 { |
| 308 gaim_proxy_connect_info_disconnect(connect_info); | 306 gaim_proxy_connect_info_disconnect(connect_info); |
| 309 | 307 |
| 310 connect_infos = g_slist_remove(connect_infos, connect_info); | 308 connect_infos = g_slist_remove(connect_infos, connect_info); |
| 311 | 309 |
| 312 /* | |
| 313 if (connect_info->query_data != NULL) | |
| 314 gaim_dnsquery_destroy(connect_info->query_data); | |
| 315 */ | |
| 316 | |
| 317 while (connect_info->hosts != NULL) | 310 while (connect_info->hosts != NULL) |
| 318 { | 311 { |
| 319 /* Discard the length... */ | 312 /* Discard the length... */ |
| 320 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); | 313 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); |
| 321 /* Free the address... */ | 314 /* Free the address... */ |
| 355 gaim_proxy_connect_info_error(GaimProxyConnectInfo *connect_info, const gchar *error_message) | 348 gaim_proxy_connect_info_error(GaimProxyConnectInfo *connect_info, const gchar *error_message) |
| 356 { | 349 { |
| 357 connect_info->connect_cb(connect_info->data, -1, error_message); | 350 connect_info->connect_cb(connect_info->data, -1, error_message); |
| 358 gaim_proxy_connect_info_destroy(connect_info); | 351 gaim_proxy_connect_info_destroy(connect_info); |
| 359 } | 352 } |
| 353 | |
| 354 #if defined(__unix__) || defined(__APPLE__) | |
| 355 | |
| 356 /* | |
| 357 * This structure represents both a pending DNS request and | |
| 358 * a free child process. | |
| 359 */ | |
| 360 typedef struct { | |
| 361 char *host; | |
| 362 int port; | |
| 363 GaimProxyDnsConnectFunction callback; | |
| 364 gpointer data; | |
| 365 guint inpa; | |
| 366 int fd_in, fd_out; | |
| 367 pid_t dns_pid; | |
| 368 } pending_dns_request_t; | |
| 369 | |
| 370 static GSList *free_dns_children = NULL; | |
| 371 static GQueue *queued_requests = NULL; | |
| 372 | |
| 373 static int number_of_dns_children = 0; | |
| 374 | |
| 375 static const int MAX_DNS_CHILDREN = 2; | |
| 376 | |
| 377 typedef struct { | |
| 378 char hostname[512]; | |
| 379 int port; | |
| 380 } dns_params_t; | |
| 381 | |
| 382 typedef struct { | |
| 383 dns_params_t params; | |
| 384 GaimProxyDnsConnectFunction callback; | |
| 385 gpointer data; | |
| 386 } queued_dns_request_t; | |
| 387 | |
| 388 /* | |
| 389 * Begin the DNS resolver child process functions. | |
| 390 */ | |
| 391 #ifdef HAVE_SIGNAL_H | |
| 392 static void | |
| 393 trap_gdb_bug() | |
| 394 { | |
| 395 const char *message = | |
| 396 "Gaim's DNS child got a SIGTRAP signal.\n" | |
| 397 "This can be caused by trying to run gaim inside gdb.\n" | |
| 398 "There is a known gdb bug which prevents this. Supposedly gaim\n" | |
| 399 "should have detected you were using gdb and used an ugly hack,\n" | |
| 400 "check cope_with_gdb_brokenness() in proxy.c.\n\n" | |
| 401 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
| 402 fputs("\n* * *\n",stderr); | |
| 403 fputs(message,stderr); | |
| 404 fputs("* * *\n\n",stderr); | |
| 405 execlp("xmessage","xmessage","-center", message, NULL); | |
| 406 _exit(1); | |
| 407 } | |
| 408 #endif | |
| 409 | |
| 410 static void | |
| 411 cope_with_gdb_brokenness() | |
| 412 { | |
| 413 #ifdef __linux__ | |
| 414 static gboolean already_done = FALSE; | |
| 415 char s[256], e[512]; | |
| 416 int n; | |
| 417 pid_t ppid; | |
| 418 | |
| 419 if(already_done) | |
| 420 return; | |
| 421 already_done = TRUE; | |
| 422 ppid = getppid(); | |
| 423 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); | |
| 424 n = readlink(s, e, sizeof(e)); | |
| 425 if(n < 0) | |
| 426 return; | |
| 427 | |
| 428 e[MIN(n,sizeof(e)-1)] = '\0'; | |
| 429 | |
| 430 if(strstr(e,"gdb")) { | |
| 431 gaim_debug_info("dns", | |
| 432 "Debugger detected, performing useless query...\n"); | |
| 433 gethostbyname("x.x.x.x.x"); | |
| 434 } | |
| 435 #endif | |
| 436 } | |
| 437 | |
| 438 static void | |
| 439 gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) | |
| 440 { | |
| 441 dns_params_t dns_params; | |
| 442 const size_t zero = 0; | |
| 443 int rc; | |
| 444 #ifdef HAVE_GETADDRINFO | |
| 445 struct addrinfo hints, *res, *tmp; | |
| 446 char servname[20]; | |
| 447 #else | |
| 448 struct sockaddr_in sin; | |
| 449 const size_t addrlen = sizeof(sin); | |
| 450 #endif | |
| 451 | |
| 452 #ifdef HAVE_SIGNAL_H | |
| 453 signal(SIGHUP, SIG_DFL); | |
| 454 signal(SIGINT, SIG_DFL); | |
| 455 signal(SIGQUIT, SIG_DFL); | |
| 456 signal(SIGCHLD, SIG_DFL); | |
| 457 signal(SIGTERM, SIG_DFL); | |
| 458 signal(SIGTRAP, trap_gdb_bug); | |
| 459 #endif | |
| 460 | |
| 461 /* | |
| 462 * We resolve 1 host name for each iteration of this | |
| 463 * while loop. | |
| 464 * | |
| 465 * The top half of this reads in the hostname and port | |
| 466 * number from the socket with our parent. The bottom | |
| 467 * half of this resolves the IP (blocking) and sends | |
| 468 * the result back to our parent, when finished. | |
| 469 */ | |
| 470 while (1) { | |
| 471 const char ch = 'Y'; | |
| 472 fd_set fds; | |
| 473 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
| 474 FD_ZERO(&fds); | |
| 475 FD_SET(child_in, &fds); | |
| 476 rc = select(child_in + 1, &fds, NULL, NULL, &tv); | |
| 477 if (!rc) { | |
| 478 if (show_debug) | |
| 479 printf("dns[%d]: nobody needs me... =(\n", getpid()); | |
| 480 break; | |
| 481 } | |
| 482 rc = read(child_in, &dns_params, sizeof(dns_params_t)); | |
| 483 if (rc < 0) { | |
| 484 perror("read()"); | |
| 485 break; | |
| 486 } | |
| 487 if (rc == 0) { | |
| 488 if (show_debug) | |
| 489 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); | |
| 490 _exit(0); | |
| 491 } | |
| 492 if (dns_params.hostname[0] == '\0') { | |
| 493 printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); | |
| 494 _exit(1); | |
| 495 } | |
| 496 /* Tell our parent that we read the data successfully */ | |
| 497 write(child_out, &ch, sizeof(ch)); | |
| 498 | |
| 499 /* We have the hostname and port, now resolve the IP */ | |
| 500 | |
| 501 #ifdef HAVE_GETADDRINFO | |
| 502 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); | |
| 503 memset(&hints, 0, sizeof(hints)); | |
| 504 | |
| 505 /* This is only used to convert a service | |
| 506 * name to a port number. As we know we are | |
| 507 * passing a number already, we know this | |
| 508 * value will not be really used by the C | |
| 509 * library. | |
| 510 */ | |
| 511 hints.ai_socktype = SOCK_STREAM; | |
| 512 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); | |
| 513 write(child_out, &rc, sizeof(rc)); | |
| 514 if (rc != 0) { | |
| 515 close(child_out); | |
| 516 if (show_debug) | |
| 517 printf("dns[%d] Error: getaddrinfo returned %d\n", | |
| 518 getpid(), rc); | |
| 519 dns_params.hostname[0] = '\0'; | |
| 520 continue; | |
| 521 } | |
| 522 tmp = res; | |
| 523 while (res) { | |
| 524 size_t ai_addrlen = res->ai_addrlen; | |
| 525 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); | |
| 526 write(child_out, res->ai_addr, res->ai_addrlen); | |
| 527 res = res->ai_next; | |
| 528 } | |
| 529 freeaddrinfo(tmp); | |
| 530 write(child_out, &zero, sizeof(zero)); | |
| 531 #else | |
| 532 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { | |
| 533 struct hostent *hp; | |
| 534 if (!(hp = gethostbyname(dns_params.hostname))) { | |
| 535 write(child_out, &h_errno, sizeof(int)); | |
| 536 close(child_out); | |
| 537 if (show_debug) | |
| 538 printf("DNS Error: %d\n", h_errno); | |
| 539 _exit(0); | |
| 540 } | |
| 541 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
| 542 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
| 543 sin.sin_family = hp->h_addrtype; | |
| 544 } else | |
| 545 sin.sin_family = AF_INET; | |
| 546 | |
| 547 sin.sin_port = htons(dns_params.port); | |
| 548 write(child_out, &addrlen, sizeof(addrlen)); | |
| 549 write(child_out, &sin, addrlen); | |
| 550 write(child_out, &zero, sizeof(zero)); | |
| 551 #endif | |
| 552 dns_params.hostname[0] = '\0'; | |
| 553 } | |
| 554 | |
| 555 close(child_out); | |
| 556 close(child_in); | |
| 557 | |
| 558 _exit(0); | |
| 559 } | |
| 560 | |
| 561 static pending_dns_request_t * | |
| 562 gaim_dns_new_resolverthread(gboolean show_debug) | |
| 563 { | |
| 564 pending_dns_request_t *req; | |
| 565 int child_out[2], child_in[2]; | |
| 566 | |
| 567 /* Create pipes for communicating with the child process */ | |
| 568 if (pipe(child_out) || pipe(child_in)) { | |
| 569 gaim_debug_error("dns", | |
| 570 "Could not create pipes: %s\n", strerror(errno)); | |
| 571 return NULL; | |
| 572 } | |
| 573 | |
| 574 req = g_new(pending_dns_request_t, 1); | |
| 575 | |
| 576 cope_with_gdb_brokenness(); | |
| 577 | |
| 578 /* Fork! */ | |
| 579 req->dns_pid = fork(); | |
| 580 | |
| 581 /* If we are the child process... */ | |
| 582 if (req->dns_pid == 0) { | |
| 583 /* We should not access the parent's side of the pipes, so close them */ | |
| 584 close(child_out[0]); | |
| 585 close(child_in[1]); | |
| 586 | |
| 587 gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); | |
| 588 /* The thread calls _exit() rather than returning, so we never get here */ | |
| 589 } | |
| 590 | |
| 591 /* We should not access the child's side of the pipes, so close them */ | |
| 592 close(child_out[1]); | |
| 593 close(child_in[0]); | |
| 594 if (req->dns_pid == -1) { | |
| 595 gaim_debug_error("dns", | |
| 596 "Could not create child process for DNS: %s\n", | |
| 597 strerror(errno)); | |
| 598 g_free(req); | |
| 599 return NULL; | |
| 600 } | |
| 601 | |
| 602 req->fd_out = child_out[0]; | |
| 603 req->fd_in = child_in[1]; | |
| 604 number_of_dns_children++; | |
| 605 gaim_debug_info("dns", | |
| 606 "Created new DNS child %d, there are now %d children.\n", | |
| 607 req->dns_pid, number_of_dns_children); | |
| 608 | |
| 609 return req; | |
| 610 } | |
| 611 /* | |
| 612 * End the DNS resolver child process functions. | |
| 613 */ | |
| 614 | |
| 615 /* | |
| 616 * Begin the functions for dealing with the DNS child processes. | |
| 617 */ | |
| 618 static void | |
| 619 req_free(pending_dns_request_t *req) | |
| 620 { | |
| 621 g_return_if_fail(req != NULL); | |
| 622 | |
| 623 close(req->fd_in); | |
| 624 close(req->fd_out); | |
| 625 | |
| 626 g_free(req->host); | |
| 627 g_free(req); | |
| 628 | |
| 629 number_of_dns_children--; | |
| 630 } | |
| 631 | |
| 632 static int | |
| 633 send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) | |
| 634 { | |
| 635 char ch; | |
| 636 int rc; | |
| 637 pid_t pid; | |
| 638 | |
| 639 /* This waitpid might return the child's PID if it has recently | |
| 640 * exited, or it might return an error if it exited "long | |
| 641 * enough" ago that it has already been reaped; in either | |
| 642 * instance, we can't use it. */ | |
| 643 if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { | |
| 644 gaim_debug_warning("dns", | |
| 645 "DNS child %d no longer exists\n", req->dns_pid); | |
| 646 return -1; | |
| 647 } else if (pid < 0) { | |
| 648 gaim_debug_warning("dns", | |
| 649 "Wait for DNS child %d failed: %s\n", | |
| 650 req->dns_pid, strerror(errno)); | |
| 651 return -1; | |
| 652 } | |
| 653 | |
| 654 /* Let's contact this lost child! */ | |
| 655 rc = write(req->fd_in, dns_params, sizeof(*dns_params)); | |
| 656 if (rc < 0) { | |
| 657 gaim_debug_error("dns", | |
| 658 "Unable to write to DNS child %d: %d\n", | |
| 659 req->dns_pid, strerror(errno)); | |
| 660 close(req->fd_in); | |
| 661 return -1; | |
| 662 } | |
| 663 | |
| 664 g_return_val_if_fail(rc == sizeof(*dns_params), -1); | |
| 665 | |
| 666 /* Did you hear me? (This avoids some race conditions) */ | |
| 667 rc = read(req->fd_out, &ch, sizeof(ch)); | |
| 668 if (rc != 1 || ch != 'Y') | |
| 669 { | |
| 670 gaim_debug_warning("dns", | |
| 671 "DNS child %d not responding. Killing it!\n", | |
| 672 req->dns_pid); | |
| 673 kill(req->dns_pid, SIGKILL); | |
| 674 return -1; | |
| 675 } | |
| 676 | |
| 677 gaim_debug_info("dns", | |
| 678 "Successfully sent DNS request to child %d\n", req->dns_pid); | |
| 679 | |
| 680 return 0; | |
| 681 } | |
| 682 | |
| 683 static void | |
| 684 host_resolved(gpointer data, gint source, GaimInputCondition cond); | |
| 685 | |
| 686 static void | |
| 687 release_dns_child(pending_dns_request_t *req) | |
| 688 { | |
| 689 g_free(req->host); | |
| 690 req->host = NULL; | |
| 691 | |
| 692 if (queued_requests && !g_queue_is_empty(queued_requests)) { | |
| 693 queued_dns_request_t *r = g_queue_pop_head(queued_requests); | |
| 694 req->host = g_strdup(r->params.hostname); | |
| 695 req->port = r->params.port; | |
| 696 req->callback = r->callback; | |
| 697 req->data = r->data; | |
| 698 | |
| 699 gaim_debug_info("dns", | |
| 700 "Processing queued DNS query for '%s' with child %d\n", | |
| 701 req->host, req->dns_pid); | |
| 702 | |
| 703 if (send_dns_request_to_child(req, &(r->params)) != 0) { | |
| 704 req_free(req); | |
| 705 req = NULL; | |
| 706 | |
| 707 gaim_debug_warning("dns", | |
| 708 "Intent of process queued query of '%s' failed, " | |
| 709 "requeueing...\n", r->params.hostname); | |
| 710 g_queue_push_head(queued_requests, r); | |
| 711 } else { | |
| 712 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
| 713 g_free(r); | |
| 714 } | |
| 715 | |
| 716 } else { | |
| 717 req->host = NULL; | |
| 718 req->callback = NULL; | |
| 719 req->data = NULL; | |
| 720 free_dns_children = g_slist_append(free_dns_children, req); | |
| 721 } | |
| 722 } | |
| 723 | |
| 724 static void | |
| 725 host_resolved(gpointer data, gint source, GaimInputCondition cond) | |
| 726 { | |
| 727 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
| 728 int rc, err; | |
| 729 GSList *hosts = NULL; | |
| 730 struct sockaddr *addr = NULL; | |
| 731 size_t addrlen; | |
| 732 | |
| 733 gaim_debug_info("dns", "Got response for '%s'\n", req->host); | |
| 734 gaim_input_remove(req->inpa); | |
| 735 | |
| 736 rc = read(req->fd_out, &err, sizeof(err)); | |
| 737 if ((rc == 4) && (err != 0)) | |
| 738 { | |
| 739 char message[1024]; | |
| 740 #ifdef HAVE_GETADDRINFO | |
| 741 g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", | |
| 742 gai_strerror(err), req->dns_pid); | |
| 743 #else | |
| 744 g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", | |
| 745 err, req->dns_pid); | |
| 746 #endif | |
| 747 gaim_debug_error("dns", "%s\n", message); | |
| 748 req->callback(NULL, req->data, message); | |
| 749 release_dns_child(req); | |
| 750 return; | |
| 751 } | |
| 752 if (rc > 0) | |
| 753 { | |
| 754 while (rc > 0) { | |
| 755 rc = read(req->fd_out, &addrlen, sizeof(addrlen)); | |
| 756 if (rc > 0 && addrlen > 0) { | |
| 757 addr = g_malloc(addrlen); | |
| 758 rc = read(req->fd_out, addr, addrlen); | |
| 759 hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); | |
| 760 hosts = g_slist_append(hosts, addr); | |
| 761 } else { | |
| 762 break; | |
| 763 } | |
| 764 } | |
| 765 } else if (rc == -1) { | |
| 766 char message[1024]; | |
| 767 g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); | |
| 768 gaim_debug_error("dns", "%s\n", message); | |
| 769 req->callback(NULL, req->data, message); | |
| 770 req_free(req); | |
| 771 return; | |
| 772 } else if (rc == 0) { | |
| 773 char message[1024]; | |
| 774 g_snprintf(message, sizeof(message), "EOF reading from DNS child"); | |
| 775 close(req->fd_out); | |
| 776 gaim_debug_error("dns", "%s\n", message); | |
| 777 req->callback(NULL, req->data, message); | |
| 778 req_free(req); | |
| 779 return; | |
| 780 } | |
| 781 | |
| 782 /* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ | |
| 783 | |
| 784 req->callback(hosts, req->data, NULL); | |
| 785 | |
| 786 release_dns_child(req); | |
| 787 } | |
| 788 /* | |
| 789 * End the functions for dealing with the DNS child processes. | |
| 790 */ | |
| 791 | |
| 792 int | |
| 793 gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) | |
| 794 { | |
| 795 pending_dns_request_t *req = NULL; | |
| 796 dns_params_t dns_params; | |
| 797 gchar *host_temp; | |
| 798 gboolean show_debug; | |
| 799 | |
| 800 show_debug = gaim_debug_is_enabled(); | |
| 801 | |
| 802 host_temp = g_strstrip(g_strdup(hostname)); | |
| 803 strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); | |
| 804 g_free(host_temp); | |
| 805 dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; | |
| 806 dns_params.port = port; | |
| 807 | |
| 808 /* | |
| 809 * If we have any children, attempt to have them perform the DNS | |
| 810 * query. If we're able to send the query to a child, then req | |
| 811 * will be set to the pending_dns_request_t. Otherwise, req will | |
| 812 * be NULL and we'll need to create a new DNS request child. | |
| 813 */ | |
| 814 while (free_dns_children != NULL) { | |
| 815 req = free_dns_children->data; | |
| 816 free_dns_children = g_slist_remove(free_dns_children, req); | |
| 817 | |
| 818 if (send_dns_request_to_child(req, &dns_params) == 0) | |
| 819 /* We found an acceptable child, yay */ | |
| 820 break; | |
| 821 | |
| 822 req_free(req); | |
| 823 req = NULL; | |
| 824 } | |
| 825 | |
| 826 /* We need to create a new DNS request child */ | |
| 827 if (req == NULL) { | |
| 828 if (number_of_dns_children >= MAX_DNS_CHILDREN) { | |
| 829 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); | |
| 830 memcpy(&(r->params), &dns_params, sizeof(dns_params)); | |
| 831 r->callback = callback; | |
| 832 r->data = data; | |
| 833 if (!queued_requests) | |
| 834 queued_requests = g_queue_new(); | |
| 835 g_queue_push_tail(queued_requests, r); | |
| 836 | |
| 837 gaim_debug_info("dns", | |
| 838 "DNS query for '%s' queued\n", dns_params.hostname); | |
| 839 | |
| 840 return 0; | |
| 841 } | |
| 842 | |
| 843 req = gaim_dns_new_resolverthread(show_debug); | |
| 844 if (req == NULL) | |
| 845 { | |
| 846 gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); | |
| 847 return -1; | |
| 848 } | |
| 849 send_dns_request_to_child(req, &dns_params); | |
| 850 } | |
| 851 | |
| 852 req->host = g_strdup(hostname); | |
| 853 req->port = port; | |
| 854 req->callback = callback; | |
| 855 req->data = data; | |
| 856 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); | |
| 857 | |
| 858 return 0; | |
| 859 } | |
| 860 | |
| 861 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ | |
| 862 | |
| 863 typedef struct _dns_tdata { | |
| 864 char *hostname; | |
| 865 int port; | |
| 866 GaimProxyDnsConnectFunction callback; | |
| 867 gpointer data; | |
| 868 GSList *hosts; | |
| 869 char *errmsg; | |
| 870 } dns_tdata; | |
| 871 | |
| 872 static gboolean dns_main_thread_cb(gpointer data) { | |
| 873 dns_tdata *td = (dns_tdata*)data; | |
| 874 if (td->errmsg != NULL) { | |
| 875 gaim_debug_info("dns", "%s\n", td->errmsg); | |
| 876 } | |
| 877 td->callback(td->hosts, td->data, td->errmsg); | |
| 878 g_free(td->hostname); | |
| 879 g_free(td->errmsg); | |
| 880 g_free(td); | |
| 881 return FALSE; | |
| 882 } | |
| 883 | |
| 884 static gpointer dns_thread(gpointer data) { | |
| 885 | |
| 886 #ifdef HAVE_GETADDRINFO | |
| 887 int rc; | |
| 888 struct addrinfo hints, *res, *tmp; | |
| 889 char servname[20]; | |
| 890 #else | |
| 891 struct sockaddr_in sin; | |
| 892 struct hostent *hp; | |
| 893 #endif | |
| 894 dns_tdata *td = (dns_tdata*)data; | |
| 895 | |
| 896 #ifdef HAVE_GETADDRINFO | |
| 897 g_snprintf(servname, sizeof(servname), "%d", td->port); | |
| 898 memset(&hints,0,sizeof(hints)); | |
| 899 | |
| 900 /* This is only used to convert a service | |
| 901 * name to a port number. As we know we are | |
| 902 * passing a number already, we know this | |
| 903 * value will not be really used by the C | |
| 904 * library. | |
| 905 */ | |
| 906 hints.ai_socktype = SOCK_STREAM; | |
| 907 if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { | |
| 908 tmp = res; | |
| 909 while(res) { | |
| 910 td->hosts = g_slist_append(td->hosts, | |
| 911 GSIZE_TO_POINTER(res->ai_addrlen)); | |
| 912 td->hosts = g_slist_append(td->hosts, | |
| 913 g_memdup(res->ai_addr, res->ai_addrlen)); | |
| 914 res = res->ai_next; | |
| 915 } | |
| 916 freeaddrinfo(tmp); | |
| 917 } else { | |
| 918 td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); | |
| 919 } | |
| 920 #else | |
| 921 if ((hp = gethostbyname(td->hostname))) { | |
| 922 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
| 923 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
| 924 sin.sin_family = hp->h_addrtype; | |
| 925 sin.sin_port = htons(td->port); | |
| 926 | |
| 927 td->hosts = g_slist_append(td->hosts, | |
| 928 GSIZE_TO_POINTER(sizeof(sin))); | |
| 929 td->hosts = g_slist_append(td->hosts, | |
| 930 g_memdup(&sin, sizeof(sin))); | |
| 931 } else { | |
| 932 td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); | |
| 933 } | |
| 934 #endif | |
| 935 /* back to main thread */ | |
| 936 g_idle_add(dns_main_thread_cb, td); | |
| 937 return 0; | |
| 938 } | |
| 939 | |
| 940 int | |
| 941 gaim_gethostbyname_async(const char *hostname, int port, | |
| 942 GaimProxyDnsConnectFunction callback, gpointer data) | |
| 943 { | |
| 944 dns_tdata *td; | |
| 945 struct sockaddr_in sin; | |
| 946 GError* err = NULL; | |
| 947 | |
| 948 if(inet_aton(hostname, &sin.sin_addr)) { | |
| 949 GSList *hosts = NULL; | |
| 950 sin.sin_family = AF_INET; | |
| 951 sin.sin_port = htons(port); | |
| 952 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); | |
| 953 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); | |
| 954 callback(hosts, data, NULL); | |
| 955 return 0; | |
| 956 } | |
| 957 | |
| 958 gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); | |
| 959 td = g_new0(dns_tdata, 1); | |
| 960 td->hostname = g_strdup(hostname); | |
| 961 td->port = port; | |
| 962 td->callback = callback; | |
| 963 td->data = data; | |
| 964 | |
| 965 if(!g_thread_create(dns_thread, td, FALSE, &err)) { | |
| 966 gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); | |
| 967 g_error_free(err); | |
| 968 g_free(td->hostname); | |
| 969 g_free(td); | |
| 970 return -1; | |
| 971 } | |
| 972 return 0; | |
| 973 } | |
| 974 | |
| 975 #else /* not __unix__ or __APPLE__ or _WIN32 */ | |
| 976 | |
| 977 typedef struct { | |
| 978 gpointer data; | |
| 979 size_t addrlen; | |
| 980 struct sockaddr *addr; | |
| 981 GaimProxyDnsConnectFunction callback; | |
| 982 } pending_dns_request_t; | |
| 983 | |
| 984 static gboolean host_resolved(gpointer data) | |
| 985 { | |
| 986 pending_dns_request_t *req = (pending_dns_request_t*)data; | |
| 987 GSList *hosts = NULL; | |
| 988 hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); | |
| 989 hosts = g_slist_append(hosts, req->addr); | |
| 990 req->callback(hosts, req->data, NULL); | |
| 991 g_free(req); | |
| 992 return FALSE; | |
| 993 } | |
| 994 | |
| 995 int | |
| 996 gaim_gethostbyname_async(const char *hostname, int port, | |
| 997 GaimProxyDnsConnectFunction callback, gpointer data) | |
| 998 { | |
| 999 struct sockaddr_in sin; | |
| 1000 pending_dns_request_t *req; | |
| 1001 | |
| 1002 if (!inet_aton(hostname, &sin.sin_addr)) { | |
| 1003 struct hostent *hp; | |
| 1004 if(!(hp = gethostbyname(hostname))) { | |
| 1005 gaim_debug_error("dns", | |
| 1006 "gaim_gethostbyname(\"%s\", %d) failed: %d\n", | |
| 1007 hostname, port, h_errno); | |
| 1008 return -1; | |
| 1009 } | |
| 1010 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
| 1011 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
| 1012 sin.sin_family = hp->h_addrtype; | |
| 1013 } else | |
| 1014 sin.sin_family = AF_INET; | |
| 1015 sin.sin_port = htons(port); | |
| 1016 | |
| 1017 req = g_new(pending_dns_request_t, 1); | |
| 1018 req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); | |
| 1019 req->addrlen = sizeof(sin); | |
| 1020 req->data = data; | |
| 1021 req->callback = callback; | |
| 1022 gaim_timeout_add(10, host_resolved, req); | |
| 1023 return 0; | |
| 1024 } | |
| 1025 | |
| 1026 #endif /* not __unix__ or __APPLE__ or _WIN32 */ | |
| 360 | 1027 |
| 361 static void | 1028 static void |
| 362 no_one_calls(gpointer data, gint source, GaimInputCondition cond) | 1029 no_one_calls(gpointer data, gint source, GaimInputCondition cond) |
| 363 { | 1030 { |
| 364 GaimProxyConnectInfo *connect_info = data; | 1031 GaimProxyConnectInfo *connect_info = data; |
| 1699 default: | 2366 default: |
| 1700 gaim_proxy_connect_info_destroy(connect_info); | 2367 gaim_proxy_connect_info_destroy(connect_info); |
| 1701 return NULL; | 2368 return NULL; |
| 1702 } | 2369 } |
| 1703 | 2370 |
| 1704 connect_info->query_data = gaim_dnsquery_a(connecthost, | 2371 if (gaim_gethostbyname_async(connecthost, |
| 1705 connectport, connection_host_resolved, connect_info); | 2372 connectport, connection_host_resolved, connect_info) != 0) |
| 1706 if (connect_info->query_data == NULL) | |
| 1707 { | 2373 { |
| 1708 gaim_proxy_connect_info_destroy(connect_info); | 2374 gaim_proxy_connect_info_destroy(connect_info); |
| 1709 return NULL; | 2375 return NULL; |
| 1710 } | 2376 } |
| 1711 | 2377 |
| 1733 connect_info->data = data; | 2399 connect_info->data = data; |
| 1734 connect_info->host = g_strdup(host); | 2400 connect_info->host = g_strdup(host); |
| 1735 connect_info->port = port; | 2401 connect_info->port = port; |
| 1736 connect_info->gpi = gpi; | 2402 connect_info->gpi = gpi; |
| 1737 | 2403 |
| 1738 connect_info->query_data = gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), | 2404 if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), |
| 1739 gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info); | 2405 gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info) != 0) |
| 1740 if (connect_info->query_data == NULL) | |
| 1741 { | 2406 { |
| 1742 gaim_proxy_connect_info_destroy(connect_info); | 2407 gaim_proxy_connect_info_destroy(connect_info); |
| 1743 return NULL; | 2408 return NULL; |
| 1744 } | 2409 } |
| 1745 | 2410 |
