Mercurial > pidgin
comparison src/proxy.c @ 14163:c3167a1dd817
[gaim-migrate @ 16811]
Split the DNS query stuff out into it's own file. Eventually
we should move the dnssrv code into this same file. Maybe
even share some code?
Also the first steps toward cancelable DNS queries.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Thu, 17 Aug 2006 07:44:52 +0000 |
| parents | 7a205b430d19 |
| children | 879bb47cff8e |
comparison
equal
deleted
inserted
replaced
| 14162:cef7f6a93592 | 14163:c3167a1dd817 |
|---|---|
| 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" | |
| 34 #include "debug.h" | 35 #include "debug.h" |
| 35 #include "notify.h" | 36 #include "notify.h" |
| 36 #include "ntlm.h" | 37 #include "ntlm.h" |
| 37 #include "prefs.h" | 38 #include "prefs.h" |
| 38 #include "proxy.h" | 39 #include "proxy.h" |
| 44 char *host; | 45 char *host; |
| 45 int port; | 46 int port; |
| 46 int fd; | 47 int fd; |
| 47 guint inpa; | 48 guint inpa; |
| 48 GaimProxyInfo *gpi; | 49 GaimProxyInfo *gpi; |
| 50 GaimDnsqueryData *query_data; | |
| 49 | 51 |
| 50 /** | 52 /** |
| 51 * This contains alternating length/char* values. The char* | 53 * This contains alternating length/char* values. The char* |
| 52 * values need to be freed when removed from the linked list. | 54 * values need to be freed when removed from the linked list. |
| 53 */ | 55 */ |
| 305 { | 307 { |
| 306 gaim_proxy_connect_info_disconnect(connect_info); | 308 gaim_proxy_connect_info_disconnect(connect_info); |
| 307 | 309 |
| 308 connect_infos = g_slist_remove(connect_infos, connect_info); | 310 connect_infos = g_slist_remove(connect_infos, connect_info); |
| 309 | 311 |
| 312 /* | |
| 313 if (connect_info->query_data != NULL) | |
| 314 gaim_dnsquery_destroy(connect_info->query_data); | |
| 315 */ | |
| 316 | |
| 310 while (connect_info->hosts != NULL) | 317 while (connect_info->hosts != NULL) |
| 311 { | 318 { |
| 312 /* Discard the length... */ | 319 /* Discard the length... */ |
| 313 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); | 320 connect_info->hosts = g_slist_remove(connect_info->hosts, connect_info->hosts->data); |
| 314 /* Free the address... */ | 321 /* Free the address... */ |
| 348 gaim_proxy_connect_info_error(GaimProxyConnectInfo *connect_info, const gchar *error_message) | 355 gaim_proxy_connect_info_error(GaimProxyConnectInfo *connect_info, const gchar *error_message) |
| 349 { | 356 { |
| 350 connect_info->connect_cb(connect_info->data, -1, error_message); | 357 connect_info->connect_cb(connect_info->data, -1, error_message); |
| 351 gaim_proxy_connect_info_destroy(connect_info); | 358 gaim_proxy_connect_info_destroy(connect_info); |
| 352 } | 359 } |
| 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 */ | |
| 1027 | 360 |
| 1028 static void | 361 static void |
| 1029 no_one_calls(gpointer data, gint source, GaimInputCondition cond) | 362 no_one_calls(gpointer data, gint source, GaimInputCondition cond) |
| 1030 { | 363 { |
| 1031 GaimProxyConnectInfo *connect_info = data; | 364 GaimProxyConnectInfo *connect_info = data; |
| 2366 default: | 1699 default: |
| 2367 gaim_proxy_connect_info_destroy(connect_info); | 1700 gaim_proxy_connect_info_destroy(connect_info); |
| 2368 return NULL; | 1701 return NULL; |
| 2369 } | 1702 } |
| 2370 | 1703 |
| 2371 if (gaim_gethostbyname_async(connecthost, | 1704 connect_info->query_data = gaim_dnsquery_a(connecthost, |
| 2372 connectport, connection_host_resolved, connect_info) != 0) | 1705 connectport, connection_host_resolved, connect_info); |
| 1706 if (connect_info->query_data == NULL) | |
| 2373 { | 1707 { |
| 2374 gaim_proxy_connect_info_destroy(connect_info); | 1708 gaim_proxy_connect_info_destroy(connect_info); |
| 2375 return NULL; | 1709 return NULL; |
| 2376 } | 1710 } |
| 2377 | 1711 |
| 2399 connect_info->data = data; | 1733 connect_info->data = data; |
| 2400 connect_info->host = g_strdup(host); | 1734 connect_info->host = g_strdup(host); |
| 2401 connect_info->port = port; | 1735 connect_info->port = port; |
| 2402 connect_info->gpi = gpi; | 1736 connect_info->gpi = gpi; |
| 2403 | 1737 |
| 2404 if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), | 1738 connect_info->query_data = gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), |
| 2405 gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info) != 0) | 1739 gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info); |
| 1740 if (connect_info->query_data == NULL) | |
| 2406 { | 1741 { |
| 2407 gaim_proxy_connect_info_destroy(connect_info); | 1742 gaim_proxy_connect_info_destroy(connect_info); |
| 2408 return NULL; | 1743 return NULL; |
| 2409 } | 1744 } |
| 2410 | 1745 |
