2 * @file dnsquery.c DNS query API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #define _PURPLE_DNSQUERY_C_
41 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
42 #define PURPLE_DNSQUERY_USE_FORK
44 /**************************************************************************
46 **************************************************************************/
48 static PurpleDnsQueryUiOps
*dns_query_ui_ops
= NULL
;
50 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess
;
52 struct _PurpleDnsQueryData
{
55 PurpleDnsQueryConnectFunction callback
;
58 PurpleAccount
*account
;
60 #if defined(PURPLE_DNSQUERY_USE_FORK)
61 PurpleDnsQueryResolverProcess
*resolver
;
62 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
69 #if defined(PURPLE_DNSQUERY_USE_FORK)
71 #define MAX_DNS_CHILDREN 4
74 * This structure keeps a reference to a child resolver process.
76 struct _PurpleDnsQueryResolverProcess
{
82 static GSList
*free_dns_children
= NULL
;
83 /* TODO: Make me a GQueue when we require >= glib 2.4 */
84 static GSList
*queued_requests
= NULL
;
86 static int number_of_dns_children
= 0;
89 * This is a convenience struct used to pass data to
90 * the child resolver process.
96 #endif /* end PURPLE_DNSQUERY_USE_FORK */
99 purple_dnsquery_resolved(PurpleDnsQueryData
*query_data
, GSList
*hosts
)
101 purple_debug_info("dnsquery", "IP resolved for %s\n", query_data
->hostname
);
102 if (query_data
->callback
!= NULL
)
103 query_data
->callback(hosts
, query_data
->data
, NULL
);
107 * Callback is a required parameter, but it can get set to
108 * NULL if we cancel a thread-based DNS lookup. So we need
111 while (hosts
!= NULL
)
113 hosts
= g_slist_remove(hosts
, hosts
->data
);
115 hosts
= g_slist_remove(hosts
, hosts
->data
);
119 #ifdef PURPLE_DNSQUERY_USE_FORK
121 * Add the resolver to the list of available resolvers, and set it
122 * to NULL so that it doesn't get destroyed along with the query_data
124 if (query_data
->resolver
)
126 free_dns_children
= g_slist_prepend(free_dns_children
, query_data
->resolver
);
127 query_data
->resolver
= NULL
;
129 #endif /* PURPLE_DNSQUERY_USE_FORK */
131 purple_dnsquery_destroy(query_data
);
135 purple_dnsquery_failed(PurpleDnsQueryData
*query_data
, const gchar
*error_message
)
137 purple_debug_error("dnsquery", "%s\n", error_message
);
138 if (query_data
->callback
!= NULL
)
139 query_data
->callback(NULL
, query_data
->data
, error_message
);
140 purple_dnsquery_destroy(query_data
);
144 purple_dnsquery_ui_resolve(PurpleDnsQueryData
*query_data
)
146 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
148 if (ops
&& ops
->resolve_host
)
149 return ops
->resolve_host(query_data
, purple_dnsquery_resolved
, purple_dnsquery_failed
);
155 resolve_ip(PurpleDnsQueryData
*query_data
)
157 #if defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST)
158 struct addrinfo hints
, *res
;
161 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
162 memset(&hints
, 0, sizeof(hints
));
163 hints
.ai_family
= AF_UNSPEC
;
164 hints
.ai_flags
|= AI_NUMERICHOST
;
166 if (0 == getaddrinfo(query_data
->hostname
, servname
, &hints
, &res
))
168 GSList
*hosts
= NULL
;
169 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(res
->ai_addrlen
));
170 hosts
= g_slist_append(hosts
, g_memdup(res
->ai_addr
, res
->ai_addrlen
));
171 purple_dnsquery_resolved(query_data
, hosts
);
176 #else /* defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) */
177 struct sockaddr_in sin
;
178 if (inet_aton(query_data
->hostname
, &sin
.sin_addr
))
181 * The given "hostname" is actually an IP address, so we
182 * don't need to do anything.
184 GSList
*hosts
= NULL
;
185 sin
.sin_family
= AF_INET
;
186 sin
.sin_port
= htons(query_data
->port
);
187 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
188 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
189 purple_dnsquery_resolved(query_data
, hosts
);
200 dns_str_is_ascii(const char *name
)
203 for (c
= (guchar
*)name
; c
&& *c
; ++c
) {
212 #if defined(PURPLE_DNSQUERY_USE_FORK)
219 * Begin the DNS resolver child process functions.
222 G_GNUC_NORETURN
static void
223 trap_gdb_bug(int sig
)
225 const char *message
=
226 "Purple's DNS child got a SIGTRAP signal.\n"
227 "This can be caused by trying to run purple inside gdb.\n"
228 "There is a known gdb bug which prevents this. Supposedly purple\n"
229 "should have detected you were using gdb and used an ugly hack,\n"
230 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n"
231 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
232 fputs("\n* * *\n",stderr
);
233 fputs(message
,stderr
);
234 fputs("* * *\n\n",stderr
);
235 execlp("xmessage","xmessage","-center", message
, NULL
);
241 write_to_parent(int fd
, const void *buf
, size_t count
)
245 written
= write(fd
, buf
, count
);
246 if (written
!= count
) {
248 fprintf(stderr
, "dns[%d]: Error writing data to "
249 "parent: %s\n", getpid(), strerror(errno
));
251 fprintf(stderr
, "dns[%d]: Error: Tried to write %"
252 G_GSIZE_FORMAT
" bytes to parent but instead "
253 "wrote %" G_GSIZE_FORMAT
" bytes\n",
254 getpid(), count
, written
);
258 G_GNUC_NORETURN
static void
259 purple_dnsquery_resolver_run(int child_out
, int child_in
, gboolean show_debug
)
261 dns_params_t dns_params
;
262 const size_t zero
= 0;
264 #ifdef HAVE_GETADDRINFO
265 struct addrinfo hints
, *res
, *tmp
;
268 struct sockaddr_in sin
;
269 const size_t addrlen
= sizeof(sin
);
274 purple_restore_default_signal_handlers();
275 signal(SIGTRAP
, trap_gdb_bug
);
279 * We resolve 1 host name for each iteration of this
282 * The top half of this reads in the hostname and port
283 * number from the socket with our parent. The bottom
284 * half of this resolves the IP (blocking) and sends
285 * the result back to our parent, when finished.
289 struct timeval tv
= { .tv_sec
= 20, .tv_usec
= 0 };
291 FD_SET(child_in
, &fds
);
292 rc
= select(child_in
+ 1, &fds
, NULL
, NULL
, &tv
);
295 printf("dns[%d]: nobody needs me... =(\n", getpid());
298 rc
= read(child_in
, &dns_params
, sizeof(dns_params_t
));
300 fprintf(stderr
, "dns[%d]: Error: Could not read dns_params: "
301 "%s\n", getpid(), strerror(errno
));
306 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
309 if (dns_params
.hostname
[0] == '\0') {
310 fprintf(stderr
, "dns[%d]: Error: Parent requested resolution "
311 "of an empty hostname (port = %d)!!!\n", getpid(),
317 if (!dns_str_is_ascii(dns_params
.hostname
)) {
318 rc
= purple_network_convert_idn_to_ascii(dns_params
.hostname
, &hostname
);
320 write_to_parent(child_out
, &rc
, sizeof(rc
));
322 fprintf(stderr
, "dns[%d] Error: IDN conversion returned "
323 "%d\n", getpid(), rc
);
324 dns_params
.hostname
[0] = '\0';
327 } else /* intentional to execute the g_strdup */
329 hostname
= g_strdup(dns_params
.hostname
);
331 /* We have the hostname and port, now resolve the IP */
333 #ifdef HAVE_GETADDRINFO
334 g_snprintf(servname
, sizeof(servname
), "%d", dns_params
.port
);
335 memset(&hints
, 0, sizeof(hints
));
337 /* This is only used to convert a service
338 * name to a port number. As we know we are
339 * passing a number already, we know this
340 * value will not be really used by the C
343 hints
.ai_socktype
= SOCK_STREAM
;
345 hints
.ai_flags
|= AI_ADDRCONFIG
;
346 #endif /* AI_ADDRCONFIG */
347 rc
= getaddrinfo(hostname
, servname
, &hints
, &res
);
348 write_to_parent(child_out
, &rc
, sizeof(rc
));
351 printf("dns[%d] Error: getaddrinfo returned %d\n",
353 dns_params
.hostname
[0] = '\0';
360 size_t ai_addrlen
= res
->ai_addrlen
;
361 write_to_parent(child_out
, &ai_addrlen
, sizeof(ai_addrlen
));
362 write_to_parent(child_out
, res
->ai_addr
, res
->ai_addrlen
);
368 if (!(hp
= gethostbyname(hostname
))) {
369 write_to_parent(child_out
, &h_errno
, sizeof(int));
372 printf("DNS Error: %d\n", h_errno
);
375 memset(&sin
, 0, sizeof(struct sockaddr_in
));
376 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
377 sin
.sin_family
= hp
->h_addrtype
;
379 sin
.sin_port
= htons(dns_params
.port
);
381 write_to_parent(child_out
, &rc
, sizeof(rc
));
382 write_to_parent(child_out
, &addrlen
, sizeof(addrlen
));
383 write_to_parent(child_out
, &sin
, addrlen
);
385 write_to_parent(child_out
, &zero
, sizeof(zero
));
386 dns_params
.hostname
[0] = '\0';
398 * End the DNS resolver child process functions.
402 * Begin the functions for dealing with the DNS child processes.
405 cope_with_gdb_brokenness(void)
408 static gboolean already_done
= FALSE
;
417 g_snprintf(s
, sizeof(s
), "/proc/%d/exe", ppid
);
418 n
= readlink(s
, e
, sizeof(e
));
422 e
[MIN(n
,sizeof(e
)-1)] = '\0';
424 if(strstr(e
,"gdb")) {
425 purple_debug_info("dns",
426 "Debugger detected, performing useless query...\n");
427 gethostbyname("x.x.x.x.x");
433 purple_dnsquery_resolver_destroy(PurpleDnsQueryResolverProcess
*resolver
)
435 g_return_if_fail(resolver
!= NULL
);
437 /* Keep this before the kill() call below. */
438 if (resolver
->inpa
!= 0) {
439 purple_input_remove(resolver
->inpa
);
444 * We might as well attempt to kill our child process. It really
445 * doesn't matter if this fails, because children will expire on
446 * their own after a few seconds.
448 if (resolver
->dns_pid
> 0)
449 kill(resolver
->dns_pid
, SIGKILL
);
451 close(resolver
->fd_in
);
452 close(resolver
->fd_out
);
456 number_of_dns_children
--;
459 static PurpleDnsQueryResolverProcess
*
460 purple_dnsquery_resolver_new(gboolean show_debug
)
462 PurpleDnsQueryResolverProcess
*resolver
;
463 int child_out
[2], child_in
[2];
465 /* Create pipes for communicating with the child process */
466 if (pipe(child_out
) || pipe(child_in
)) {
467 purple_debug_error("dns",
468 "Could not create pipes: %s\n", g_strerror(errno
));
472 resolver
= g_new(PurpleDnsQueryResolverProcess
, 1);
475 cope_with_gdb_brokenness();
477 /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */
478 resolver
->dns_pid
= fork();
480 /* If we are the child process... */
481 if (resolver
->dns_pid
== 0) {
482 /* We should not access the parent's side of the pipes, so close them */
486 purple_dnsquery_resolver_run(child_out
[1], child_in
[0], show_debug
);
487 /* The thread calls _exit() rather than returning, so we never get here */
490 /* We should not access the child's side of the pipes, so close them */
493 if (resolver
->dns_pid
== -1) {
494 purple_debug_error("dns",
495 "Could not create child process for DNS: %s\n",
497 purple_dnsquery_resolver_destroy(resolver
);
501 resolver
->fd_out
= child_out
[0];
502 resolver
->fd_in
= child_in
[1];
503 number_of_dns_children
++;
504 purple_debug_info("dns",
505 "Created new DNS child %d, there are now %d children.\n",
506 resolver
->dns_pid
, number_of_dns_children
);
512 * @return TRUE if the request was sent succesfully. FALSE
513 * if the request could not be sent. This isn't
514 * necessarily an error. If the child has expired,
515 * for example, we won't be able to send the message.
518 send_dns_request_to_child(PurpleDnsQueryData
*query_data
,
519 PurpleDnsQueryResolverProcess
*resolver
)
522 dns_params_t dns_params
;
525 /* This waitpid might return the child's PID if it has recently
526 * exited, or it might return an error if it exited "long
527 * enough" ago that it has already been reaped; in either
528 * instance, we can't use it. */
529 pid
= waitpid(resolver
->dns_pid
, NULL
, WNOHANG
);
531 purple_debug_warning("dns", "DNS child %d no longer exists\n",
533 purple_dnsquery_resolver_destroy(resolver
);
535 } else if (pid
< 0) {
536 purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n",
537 resolver
->dns_pid
, g_strerror(errno
));
538 purple_dnsquery_resolver_destroy(resolver
);
542 /* Copy the hostname and port into a single data structure */
543 strncpy(dns_params
.hostname
, query_data
->hostname
, sizeof(dns_params
.hostname
) - 1);
544 dns_params
.hostname
[sizeof(dns_params
.hostname
) - 1] = '\0';
545 dns_params
.port
= query_data
->port
;
547 /* Send the data structure to the child */
548 rc
= write(resolver
->fd_in
, &dns_params
, sizeof(dns_params
));
550 purple_debug_error("dns", "Unable to write to DNS child %d: %s\n",
551 resolver
->dns_pid
, g_strerror(errno
));
552 purple_dnsquery_resolver_destroy(resolver
);
555 if (rc
< sizeof(dns_params
)) {
556 purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
557 " bytes to child but only wrote %" G_GSSIZE_FORMAT
"\n",
558 sizeof(dns_params
), rc
);
559 purple_dnsquery_resolver_destroy(resolver
);
563 purple_debug_info("dns",
564 "Successfully sent DNS request to child %d\n",
567 query_data
->resolver
= resolver
;
572 static void host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
);
575 handle_next_queued_request(void)
577 PurpleDnsQueryData
*query_data
;
578 PurpleDnsQueryResolverProcess
*resolver
;
580 if (queued_requests
== NULL
)
581 /* No more DNS queries, yay! */
584 query_data
= queued_requests
->data
;
585 queued_requests
= g_slist_delete_link(queued_requests
, queued_requests
);
588 * If we have any children, attempt to have them perform the DNS
589 * query. If we're able to send the query then resolver will be
590 * set to the PurpleDnsQueryResolverProcess. Otherwise, resolver
591 * will be NULL and we'll need to create a new DNS request child.
593 while (free_dns_children
!= NULL
)
595 resolver
= free_dns_children
->data
;
596 free_dns_children
= g_slist_remove(free_dns_children
, resolver
);
598 if (send_dns_request_to_child(query_data
, resolver
))
599 /* We found an acceptable child, yay */
603 /* We need to create a new DNS request child */
604 if (query_data
->resolver
== NULL
)
606 if (number_of_dns_children
>= MAX_DNS_CHILDREN
)
608 /* Apparently all our children are busy */
609 queued_requests
= g_slist_prepend(queued_requests
, query_data
);
613 resolver
= purple_dnsquery_resolver_new(purple_debug_is_enabled());
614 if (resolver
== NULL
)
616 purple_dnsquery_failed(query_data
, _("Unable to create new resolver process\n"));
619 if (!send_dns_request_to_child(query_data
, resolver
))
621 purple_dnsquery_failed(query_data
, _("Unable to send request to resolver process\n"));
626 query_data
->resolver
->inpa
= purple_input_add(query_data
->resolver
->fd_out
,
627 PURPLE_INPUT_READ
, host_resolved
, query_data
);
631 * End the functions for dealing with the DNS child processes.
635 host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
)
637 PurpleDnsQueryData
*query_data
;
639 GSList
*hosts
= NULL
;
640 struct sockaddr
*addr
= NULL
;
646 purple_debug_info("dns", "Got response for '%s'\n", query_data
->hostname
);
647 purple_input_remove(query_data
->resolver
->inpa
);
648 query_data
->resolver
->inpa
= 0;
650 rc
= read(query_data
->resolver
->fd_out
, &err
, sizeof(err
));
651 if ((rc
== 4) && (err
!= 0))
653 #ifdef HAVE_GETADDRINFO
654 g_snprintf(message
, sizeof(message
), _("Error resolving %s:\n%s"),
655 query_data
->hostname
, purple_gai_strerror(err
));
657 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
658 query_data
->hostname
, err
);
660 /* Re-read resolv.conf and friends in case DNS servers have changed */
663 purple_dnsquery_failed(query_data
, message
);
667 rc
= read(query_data
->resolver
->fd_out
, &addrlen
, sizeof(addrlen
));
668 if (rc
> 0 && addrlen
> 0) {
669 addr
= g_malloc(addrlen
);
670 rc
= read(query_data
->resolver
->fd_out
, addr
, addrlen
);
671 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(addrlen
));
672 hosts
= g_slist_append(hosts
, addr
);
677 /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */
678 purple_dnsquery_resolved(query_data
, hosts
);
680 } else if (rc
== -1) {
681 g_snprintf(message
, sizeof(message
), _("Error reading from resolver process:\n%s"), g_strerror(errno
));
682 purple_dnsquery_failed(query_data
, message
);
684 } else if (rc
== 0) {
685 g_snprintf(message
, sizeof(message
), _("Resolver process exited without answering our request"));
686 purple_dnsquery_failed(query_data
, message
);
689 handle_next_queued_request();
693 resolve_host(PurpleDnsQueryData
*query_data
)
695 queued_requests
= g_slist_append(queued_requests
, query_data
);
697 handle_next_queued_request();
700 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
707 dns_main_thread_cb(gpointer data
)
709 PurpleDnsQueryData
*query_data
= data
;
711 /* We're done, so purple_dnsquery_destroy() shouldn't think it is canceling an in-progress lookup */
712 query_data
->resolver
= NULL
;
714 if (query_data
->error_message
!= NULL
)
715 purple_dnsquery_failed(query_data
, query_data
->error_message
);
720 /* We don't want purple_dns_query_resolved() to free(hosts) */
721 hosts
= query_data
->hosts
;
722 query_data
->hosts
= NULL
;
723 purple_dnsquery_resolved(query_data
, hosts
);
730 dns_thread(gpointer data
)
732 PurpleDnsQueryData
*query_data
;
733 #ifdef HAVE_GETADDRINFO
735 struct addrinfo hints
, *res
, *tmp
;
738 struct sockaddr_in sin
;
746 if (!dns_str_is_ascii(query_data
->hostname
)) {
747 rc
= purple_network_convert_idn_to_ascii(query_data
->hostname
, &hostname
);
749 query_data
->error_message
= g_strdup_printf(_("Error converting %s "
750 "to punycode: %d"), query_data
->hostname
, rc
);
751 /* back to main thread */
752 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
755 } else /* intentional fallthru */
757 hostname
= g_strdup(query_data
->hostname
);
759 #ifdef HAVE_GETADDRINFO
760 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
761 memset(&hints
,0,sizeof(hints
));
764 * This is only used to convert a service
765 * name to a port number. As we know we are
766 * passing a number already, we know this
767 * value will not be really used by the C
770 hints
.ai_socktype
= SOCK_STREAM
;
772 hints
.ai_flags
|= AI_ADDRCONFIG
;
773 #endif /* AI_ADDRCONFIG */
774 if ((rc
= getaddrinfo(hostname
, servname
, &hints
, &res
)) == 0) {
777 query_data
->hosts
= g_slist_append(query_data
->hosts
,
778 GSIZE_TO_POINTER(res
->ai_addrlen
));
779 query_data
->hosts
= g_slist_append(query_data
->hosts
,
780 g_memdup(res
->ai_addr
, res
->ai_addrlen
));
785 query_data
->error_message
= g_strdup_printf(_("Error resolving %s:\n%s"), query_data
->hostname
, purple_gai_strerror(rc
));
788 if ((hp
= gethostbyname(hostname
))) {
789 memset(&sin
, 0, sizeof(struct sockaddr_in
));
790 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
791 sin
.sin_family
= hp
->h_addrtype
;
792 sin
.sin_port
= htons(query_data
->port
);
794 query_data
->hosts
= g_slist_append(query_data
->hosts
,
795 GSIZE_TO_POINTER(sizeof(sin
)));
796 query_data
->hosts
= g_slist_append(query_data
->hosts
,
797 g_memdup(&sin
, sizeof(sin
)));
799 query_data
->error_message
= g_strdup_printf(_("Error resolving %s: %d"), query_data
->hostname
, h_errno
);
804 /* back to main thread */
805 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
811 resolve_host(PurpleDnsQueryData
*query_data
)
816 * Spin off a separate thread to perform the DNS lookup so
817 * that we don't block the UI.
819 query_data
->resolver
= g_thread_create(dns_thread
,
820 query_data
, FALSE
, &err
);
821 if (query_data
->resolver
== NULL
)
824 g_snprintf(message
, sizeof(message
), _("Thread creation failure: %s"),
825 (err
&& err
->message
) ? err
->message
: _("Unknown reason"));
827 purple_dnsquery_failed(query_data
, message
);
831 #else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
834 * We weren't able to do anything fancier above, so use the
835 * fail-safe name resolution code, which is blocking.
839 resolve_host(PurpleDnsQueryData
*query_data
)
841 struct sockaddr_in sin
;
842 GSList
*hosts
= NULL
;
846 if (!dns_str_is_ascii(query_data
->hostname
)) {
847 int ret
= purple_network_convert_idn_to_ascii(query_data
->hostname
,
851 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
852 query_data
->hostname
, ret
);
853 purple_dnsquery_failed(query_data
, message
);
856 } else /* fallthrough is intentional to the g_strdup */
858 hostname
= g_strdup(query_data
->hostname
);
860 if(!(hp
= gethostbyname(hostname
))) {
862 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
863 query_data
->hostname
, h_errno
);
864 purple_dnsquery_failed(query_data
, message
);
868 memset(&sin
, 0, sizeof(struct sockaddr_in
));
869 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
870 sin
.sin_family
= hp
->h_addrtype
;
872 sin
.sin_port
= htons(query_data
->port
);
874 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
875 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
877 purple_dnsquery_resolved(query_data
, hosts
);
880 #endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
883 initiate_resolving(gpointer data
)
885 PurpleDnsQueryData
*query_data
;
886 PurpleProxyType proxy_type
;
889 query_data
->timeout
= 0;
891 if (resolve_ip(query_data
))
892 /* resolve_ip calls purple_dnsquery_resolved */
895 proxy_type
= purple_proxy_info_get_type(
896 purple_proxy_get_setup(query_data
->account
));
897 if (proxy_type
== PURPLE_PROXY_TOR
) {
898 purple_dnsquery_failed(query_data
,
899 _("Aborting DNS lookup in Tor Proxy mode."));
903 if (purple_dnsquery_ui_resolve(query_data
))
904 /* The UI is handling the resolve; we're done */
907 resolve_host(query_data
);
913 purple_dnsquery_a_account(PurpleAccount
*account
, const char *hostname
, int port
,
914 PurpleDnsQueryConnectFunction callback
, gpointer data
)
916 PurpleDnsQueryData
*query_data
;
918 g_return_val_if_fail(hostname
!= NULL
, NULL
);
919 g_return_val_if_fail(port
!= 0, NULL
);
920 g_return_val_if_fail(callback
!= NULL
, NULL
);
922 purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname
);
924 query_data
= g_new0(PurpleDnsQueryData
, 1);
925 query_data
->hostname
= g_strdup(hostname
);
926 g_strstrip(query_data
->hostname
);
927 query_data
->port
= port
;
928 query_data
->callback
= callback
;
929 query_data
->data
= data
;
930 query_data
->account
= account
;
932 if (*query_data
->hostname
== '\0')
934 purple_dnsquery_destroy(query_data
);
935 g_return_val_if_reached(NULL
);
938 query_data
->timeout
= purple_timeout_add(0, initiate_resolving
, query_data
);
944 purple_dnsquery_a(const char *hostname
, int port
,
945 PurpleDnsQueryConnectFunction callback
, gpointer data
)
947 return purple_dnsquery_a_account(NULL
, hostname
, port
, callback
, data
);
951 purple_dnsquery_destroy(PurpleDnsQueryData
*query_data
)
953 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
955 if (ops
&& ops
->destroy
)
956 ops
->destroy(query_data
);
958 #if defined(PURPLE_DNSQUERY_USE_FORK)
959 queued_requests
= g_slist_remove(queued_requests
, query_data
);
961 if (query_data
->resolver
!= NULL
)
963 * This is only non-NULL when we're cancelling an in-progress
964 * query. Ideally we would tell our resolver child to stop
965 * resolving shit and then we would add it back to the
966 * free_dns_children linked list. However, it's hard to tell
967 * children stuff, they just don't listen. So we'll just
968 * kill the process and allow a new child to be started if we
969 * have more stuff to resolve.
971 purple_dnsquery_resolver_destroy(query_data
->resolver
);
972 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
973 if (query_data
->resolver
!= NULL
)
976 * It's not really possible to kill a thread. So instead we
977 * just set the callback to NULL and let the DNS lookup
980 query_data
->callback
= NULL
;
984 while (query_data
->hosts
!= NULL
)
986 /* Discard the length... */
987 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
988 /* Free the address... */
989 g_free(query_data
->hosts
->data
);
990 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
992 g_free(query_data
->error_message
);
993 #endif /* end _WIN32 */
995 if (query_data
->timeout
> 0)
996 purple_timeout_remove(query_data
->timeout
);
998 g_free(query_data
->hostname
);
1003 purple_dnsquery_get_host(PurpleDnsQueryData
*query_data
)
1005 g_return_val_if_fail(query_data
!= NULL
, NULL
);
1007 return query_data
->hostname
;
1011 purple_dnsquery_get_port(PurpleDnsQueryData
*query_data
)
1013 g_return_val_if_fail(query_data
!= NULL
, 0);
1015 return query_data
->port
;
1019 purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps
*ops
)
1021 dns_query_ui_ops
= ops
;
1024 PurpleDnsQueryUiOps
*
1025 purple_dnsquery_get_ui_ops(void)
1027 /* It is perfectly acceptable for dns_query_ui_ops to be NULL; this just
1028 * means that the default platform-specific implementation will be used.
1030 return dns_query_ui_ops
;
1034 purple_dnsquery_init(void)
1039 purple_dnsquery_uninit(void)
1041 #if defined(PURPLE_DNSQUERY_USE_FORK)
1042 while (free_dns_children
!= NULL
)
1044 purple_dnsquery_resolver_destroy(free_dns_children
->data
);
1045 free_dns_children
= g_slist_remove(free_dns_children
, free_dns_children
->data
);
1047 #endif /* end PURPLE_DNSQUERY_USE_FORK */