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
40 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
41 #define PURPLE_DNSQUERY_USE_FORK
43 /**************************************************************************
45 **************************************************************************/
47 static PurpleDnsQueryUiOps
*dns_query_ui_ops
= NULL
;
49 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess
;
51 struct _PurpleDnsQueryData
{
54 PurpleDnsQueryConnectFunction callback
;
58 #if defined(PURPLE_DNSQUERY_USE_FORK)
59 PurpleDnsQueryResolverProcess
*resolver
;
60 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
67 #if defined(PURPLE_DNSQUERY_USE_FORK)
69 #define MAX_DNS_CHILDREN 4
72 * This structure keeps a reference to a child resolver process.
74 struct _PurpleDnsQueryResolverProcess
{
80 static GSList
*free_dns_children
= NULL
;
81 /* TODO: Make me a GQueue when we require >= glib 2.4 */
82 static GSList
*queued_requests
= NULL
;
84 static int number_of_dns_children
= 0;
87 * This is a convenience struct used to pass data to
88 * the child resolver process.
94 #endif /* end PURPLE_DNSQUERY_USE_FORK */
97 purple_dnsquery_resolved(PurpleDnsQueryData
*query_data
, GSList
*hosts
)
99 purple_debug_info("dnsquery", "IP resolved for %s\n", query_data
->hostname
);
100 if (query_data
->callback
!= NULL
)
101 query_data
->callback(hosts
, query_data
->data
, NULL
);
105 * Callback is a required parameter, but it can get set to
106 * NULL if we cancel a thread-based DNS lookup. So we need
109 while (hosts
!= NULL
)
111 hosts
= g_slist_remove(hosts
, hosts
->data
);
113 hosts
= g_slist_remove(hosts
, hosts
->data
);
117 #ifdef PURPLE_DNSQUERY_USE_FORK
119 * Add the resolver to the list of available resolvers, and set it
120 * to NULL so that it doesn't get destroyed along with the query_data
122 if (query_data
->resolver
)
124 free_dns_children
= g_slist_prepend(free_dns_children
, query_data
->resolver
);
125 query_data
->resolver
= NULL
;
127 #endif /* PURPLE_DNSQUERY_USE_FORK */
129 purple_dnsquery_destroy(query_data
);
133 purple_dnsquery_failed(PurpleDnsQueryData
*query_data
, const gchar
*error_message
)
135 purple_debug_error("dnsquery", "%s\n", error_message
);
136 if (query_data
->callback
!= NULL
)
137 query_data
->callback(NULL
, query_data
->data
, error_message
);
138 purple_dnsquery_destroy(query_data
);
142 purple_dnsquery_ui_resolve(PurpleDnsQueryData
*query_data
)
144 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
146 if (ops
&& ops
->resolve_host
)
147 return ops
->resolve_host(query_data
, purple_dnsquery_resolved
, purple_dnsquery_failed
);
153 resolve_ip(PurpleDnsQueryData
*query_data
)
155 struct sockaddr_in sin
;
156 if (inet_aton(query_data
->hostname
, &sin
.sin_addr
))
159 * The given "hostname" is actually an IP address, so we
160 * don't need to do anything.
162 GSList
*hosts
= NULL
;
163 sin
.sin_family
= AF_INET
;
164 sin
.sin_port
= htons(query_data
->port
);
165 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
166 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
167 purple_dnsquery_resolved(query_data
, hosts
);
177 dns_str_is_ascii(const char *name
)
180 for (c
= (guchar
*)name
; c
&& *c
; ++c
) {
189 #if defined(PURPLE_DNSQUERY_USE_FORK)
196 * Begin the DNS resolver child process functions.
199 G_GNUC_NORETURN
static void
200 trap_gdb_bug(int sig
)
202 const char *message
=
203 "Purple's DNS child got a SIGTRAP signal.\n"
204 "This can be caused by trying to run purple inside gdb.\n"
205 "There is a known gdb bug which prevents this. Supposedly purple\n"
206 "should have detected you were using gdb and used an ugly hack,\n"
207 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n"
208 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
209 fputs("\n* * *\n",stderr
);
210 fputs(message
,stderr
);
211 fputs("* * *\n\n",stderr
);
212 execlp("xmessage","xmessage","-center", message
, NULL
);
218 write_to_parent(int fd
, const void *buf
, size_t count
)
222 written
= write(fd
, buf
, count
);
223 if (written
!= count
) {
225 fprintf(stderr
, "dns[%d]: Error writing data to "
226 "parent: %s\n", getpid(), strerror(errno
));
228 fprintf(stderr
, "dns[%d]: Error: Tried to write %"
229 G_GSIZE_FORMAT
" bytes to parent but instead "
230 "wrote %" G_GSIZE_FORMAT
" bytes\n",
231 getpid(), count
, written
);
235 G_GNUC_NORETURN
static void
236 purple_dnsquery_resolver_run(int child_out
, int child_in
, gboolean show_debug
)
238 dns_params_t dns_params
;
239 const size_t zero
= 0;
241 #ifdef HAVE_GETADDRINFO
242 struct addrinfo hints
, *res
, *tmp
;
245 struct sockaddr_in sin
;
246 const size_t addrlen
= sizeof(sin
);
251 purple_restore_default_signal_handlers();
252 signal(SIGTRAP
, trap_gdb_bug
);
256 * We resolve 1 host name for each iteration of this
259 * The top half of this reads in the hostname and port
260 * number from the socket with our parent. The bottom
261 * half of this resolves the IP (blocking) and sends
262 * the result back to our parent, when finished.
266 struct timeval tv
= { .tv_sec
= 20, .tv_usec
= 0 };
268 FD_SET(child_in
, &fds
);
269 rc
= select(child_in
+ 1, &fds
, NULL
, NULL
, &tv
);
272 printf("dns[%d]: nobody needs me... =(\n", getpid());
275 rc
= read(child_in
, &dns_params
, sizeof(dns_params_t
));
277 fprintf(stderr
, "dns[%d]: Error: Could not read dns_params: "
278 "%s\n", getpid(), strerror(errno
));
283 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
286 if (dns_params
.hostname
[0] == '\0') {
287 fprintf(stderr
, "dns[%d]: Error: Parent requested resolution "
288 "of an empty hostname (port = %d)!!!\n", getpid(),
294 if (!dns_str_is_ascii(dns_params
.hostname
)) {
295 rc
= purple_network_convert_idn_to_ascii(dns_params
.hostname
, &hostname
);
297 write_to_parent(child_out
, &rc
, sizeof(rc
));
299 fprintf(stderr
, "dns[%d] Error: IDN conversion returned "
300 "%d\n", getpid(), rc
);
301 dns_params
.hostname
[0] = '\0';
304 } else /* intentional to execute the g_strdup */
306 hostname
= g_strdup(dns_params
.hostname
);
308 /* We have the hostname and port, now resolve the IP */
310 #ifdef HAVE_GETADDRINFO
311 g_snprintf(servname
, sizeof(servname
), "%d", dns_params
.port
);
312 memset(&hints
, 0, sizeof(hints
));
314 /* This is only used to convert a service
315 * name to a port number. As we know we are
316 * passing a number already, we know this
317 * value will not be really used by the C
320 hints
.ai_socktype
= SOCK_STREAM
;
322 hints
.ai_flags
|= AI_ADDRCONFIG
;
323 #endif /* AI_ADDRCONFIG */
324 rc
= getaddrinfo(hostname
, servname
, &hints
, &res
);
325 write_to_parent(child_out
, &rc
, sizeof(rc
));
328 printf("dns[%d] Error: getaddrinfo returned %d\n",
330 dns_params
.hostname
[0] = '\0';
337 size_t ai_addrlen
= res
->ai_addrlen
;
338 write_to_parent(child_out
, &ai_addrlen
, sizeof(ai_addrlen
));
339 write_to_parent(child_out
, res
->ai_addr
, res
->ai_addrlen
);
344 if (!inet_aton(hostname
, &sin
.sin_addr
)) {
346 if (!(hp
= gethostbyname(hostname
))) {
347 write_to_parent(child_out
, &h_errno
, sizeof(int));
350 printf("DNS Error: %d\n", h_errno
);
353 memset(&sin
, 0, sizeof(struct sockaddr_in
));
354 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
355 sin
.sin_family
= hp
->h_addrtype
;
357 sin
.sin_family
= AF_INET
;
359 sin
.sin_port
= htons(dns_params
.port
);
361 write_to_parent(child_out
, &rc
, sizeof(rc
));
362 write_to_parent(child_out
, &addrlen
, sizeof(addrlen
));
363 write_to_parent(child_out
, &sin
, addrlen
);
365 write_to_parent(child_out
, &zero
, sizeof(zero
));
366 dns_params
.hostname
[0] = '\0';
378 * End the DNS resolver child process functions.
382 * Begin the functions for dealing with the DNS child processes.
385 cope_with_gdb_brokenness(void)
388 static gboolean already_done
= FALSE
;
397 g_snprintf(s
, sizeof(s
), "/proc/%d/exe", ppid
);
398 n
= readlink(s
, e
, sizeof(e
));
402 e
[MIN(n
,sizeof(e
)-1)] = '\0';
404 if(strstr(e
,"gdb")) {
405 purple_debug_info("dns",
406 "Debugger detected, performing useless query...\n");
407 gethostbyname("x.x.x.x.x");
413 purple_dnsquery_resolver_destroy(PurpleDnsQueryResolverProcess
*resolver
)
415 g_return_if_fail(resolver
!= NULL
);
417 /* Keep this before the kill() call below. */
418 if (resolver
->inpa
!= 0) {
419 purple_input_remove(resolver
->inpa
);
424 * We might as well attempt to kill our child process. It really
425 * doesn't matter if this fails, because children will expire on
426 * their own after a few seconds.
428 if (resolver
->dns_pid
> 0)
429 kill(resolver
->dns_pid
, SIGKILL
);
431 close(resolver
->fd_in
);
432 close(resolver
->fd_out
);
436 number_of_dns_children
--;
439 static PurpleDnsQueryResolverProcess
*
440 purple_dnsquery_resolver_new(gboolean show_debug
)
442 PurpleDnsQueryResolverProcess
*resolver
;
443 int child_out
[2], child_in
[2];
445 /* Create pipes for communicating with the child process */
446 if (pipe(child_out
) || pipe(child_in
)) {
447 purple_debug_error("dns",
448 "Could not create pipes: %s\n", g_strerror(errno
));
452 resolver
= g_new(PurpleDnsQueryResolverProcess
, 1);
455 cope_with_gdb_brokenness();
457 /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */
458 resolver
->dns_pid
= fork();
460 /* If we are the child process... */
461 if (resolver
->dns_pid
== 0) {
462 /* We should not access the parent's side of the pipes, so close them */
466 purple_dnsquery_resolver_run(child_out
[1], child_in
[0], show_debug
);
467 /* The thread calls _exit() rather than returning, so we never get here */
470 /* We should not access the child's side of the pipes, so close them */
473 if (resolver
->dns_pid
== -1) {
474 purple_debug_error("dns",
475 "Could not create child process for DNS: %s\n",
477 purple_dnsquery_resolver_destroy(resolver
);
481 resolver
->fd_out
= child_out
[0];
482 resolver
->fd_in
= child_in
[1];
483 number_of_dns_children
++;
484 purple_debug_info("dns",
485 "Created new DNS child %d, there are now %d children.\n",
486 resolver
->dns_pid
, number_of_dns_children
);
492 * @return TRUE if the request was sent succesfully. FALSE
493 * if the request could not be sent. This isn't
494 * necessarily an error. If the child has expired,
495 * for example, we won't be able to send the message.
498 send_dns_request_to_child(PurpleDnsQueryData
*query_data
,
499 PurpleDnsQueryResolverProcess
*resolver
)
502 dns_params_t dns_params
;
505 /* This waitpid might return the child's PID if it has recently
506 * exited, or it might return an error if it exited "long
507 * enough" ago that it has already been reaped; in either
508 * instance, we can't use it. */
509 pid
= waitpid(resolver
->dns_pid
, NULL
, WNOHANG
);
511 purple_debug_warning("dns", "DNS child %d no longer exists\n",
513 purple_dnsquery_resolver_destroy(resolver
);
515 } else if (pid
< 0) {
516 purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n",
517 resolver
->dns_pid
, g_strerror(errno
));
518 purple_dnsquery_resolver_destroy(resolver
);
522 /* Copy the hostname and port into a single data structure */
523 strncpy(dns_params
.hostname
, query_data
->hostname
, sizeof(dns_params
.hostname
) - 1);
524 dns_params
.hostname
[sizeof(dns_params
.hostname
) - 1] = '\0';
525 dns_params
.port
= query_data
->port
;
527 /* Send the data structure to the child */
528 rc
= write(resolver
->fd_in
, &dns_params
, sizeof(dns_params
));
530 purple_debug_error("dns", "Unable to write to DNS child %d: %s\n",
531 resolver
->dns_pid
, g_strerror(errno
));
532 purple_dnsquery_resolver_destroy(resolver
);
535 if (rc
< sizeof(dns_params
)) {
536 purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
537 " bytes to child but only wrote %" G_GSSIZE_FORMAT
"\n",
538 sizeof(dns_params
), rc
);
539 purple_dnsquery_resolver_destroy(resolver
);
543 purple_debug_info("dns",
544 "Successfully sent DNS request to child %d\n",
547 query_data
->resolver
= resolver
;
552 static void host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
);
555 handle_next_queued_request(void)
557 PurpleDnsQueryData
*query_data
;
558 PurpleDnsQueryResolverProcess
*resolver
;
560 if (queued_requests
== NULL
)
561 /* No more DNS queries, yay! */
564 query_data
= queued_requests
->data
;
565 queued_requests
= g_slist_delete_link(queued_requests
, queued_requests
);
568 * If we have any children, attempt to have them perform the DNS
569 * query. If we're able to send the query then resolver will be
570 * set to the PurpleDnsQueryResolverProcess. Otherwise, resolver
571 * will be NULL and we'll need to create a new DNS request child.
573 while (free_dns_children
!= NULL
)
575 resolver
= free_dns_children
->data
;
576 free_dns_children
= g_slist_remove(free_dns_children
, resolver
);
578 if (send_dns_request_to_child(query_data
, resolver
))
579 /* We found an acceptable child, yay */
583 /* We need to create a new DNS request child */
584 if (query_data
->resolver
== NULL
)
586 if (number_of_dns_children
>= MAX_DNS_CHILDREN
)
588 /* Apparently all our children are busy */
589 queued_requests
= g_slist_prepend(queued_requests
, query_data
);
593 resolver
= purple_dnsquery_resolver_new(purple_debug_is_enabled());
594 if (resolver
== NULL
)
596 purple_dnsquery_failed(query_data
, _("Unable to create new resolver process\n"));
599 if (!send_dns_request_to_child(query_data
, resolver
))
601 purple_dnsquery_failed(query_data
, _("Unable to send request to resolver process\n"));
606 query_data
->resolver
->inpa
= purple_input_add(query_data
->resolver
->fd_out
,
607 PURPLE_INPUT_READ
, host_resolved
, query_data
);
611 * End the functions for dealing with the DNS child processes.
615 host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
)
617 PurpleDnsQueryData
*query_data
;
619 GSList
*hosts
= NULL
;
620 struct sockaddr
*addr
= NULL
;
626 purple_debug_info("dns", "Got response for '%s'\n", query_data
->hostname
);
627 purple_input_remove(query_data
->resolver
->inpa
);
628 query_data
->resolver
->inpa
= 0;
630 rc
= read(query_data
->resolver
->fd_out
, &err
, sizeof(err
));
631 if ((rc
== 4) && (err
!= 0))
633 #ifdef HAVE_GETADDRINFO
634 g_snprintf(message
, sizeof(message
), _("Error resolving %s:\n%s"),
635 query_data
->hostname
, purple_gai_strerror(err
));
637 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
638 query_data
->hostname
, err
);
640 /* Re-read resolv.conf and friends in case DNS servers have changed */
643 purple_dnsquery_failed(query_data
, message
);
647 rc
= read(query_data
->resolver
->fd_out
, &addrlen
, sizeof(addrlen
));
648 if (rc
> 0 && addrlen
> 0) {
649 addr
= g_malloc(addrlen
);
650 rc
= read(query_data
->resolver
->fd_out
, addr
, addrlen
);
651 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(addrlen
));
652 hosts
= g_slist_append(hosts
, addr
);
657 /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */
658 purple_dnsquery_resolved(query_data
, hosts
);
660 } else if (rc
== -1) {
661 g_snprintf(message
, sizeof(message
), _("Error reading from resolver process:\n%s"), g_strerror(errno
));
662 purple_dnsquery_failed(query_data
, message
);
664 } else if (rc
== 0) {
665 g_snprintf(message
, sizeof(message
), _("Resolver process exited without answering our request"));
666 purple_dnsquery_failed(query_data
, message
);
669 handle_next_queued_request();
673 resolve_host(gpointer data
)
675 PurpleDnsQueryData
*query_data
;
678 query_data
->timeout
= 0;
680 if (resolve_ip(query_data
))
682 /* resolve_ip calls purple_dnsquery_resolved */
686 if (purple_dnsquery_ui_resolve(query_data
))
688 /* The UI is handling the resolve; we're done */
692 queued_requests
= g_slist_append(queued_requests
, query_data
);
694 handle_next_queued_request();
700 purple_dnsquery_a(const char *hostname
, int port
,
701 PurpleDnsQueryConnectFunction callback
, gpointer data
)
703 PurpleDnsQueryData
*query_data
;
705 g_return_val_if_fail(hostname
!= NULL
, NULL
);
706 g_return_val_if_fail(port
!= 0, NULL
);
707 g_return_val_if_fail(callback
!= NULL
, NULL
);
709 query_data
= g_new(PurpleDnsQueryData
, 1);
710 query_data
->hostname
= g_strdup(hostname
);
711 g_strstrip(query_data
->hostname
);
712 query_data
->port
= port
;
713 query_data
->callback
= callback
;
714 query_data
->data
= data
;
715 query_data
->resolver
= NULL
;
717 if (*query_data
->hostname
== '\0')
719 purple_dnsquery_destroy(query_data
);
720 g_return_val_if_reached(NULL
);
723 purple_debug_info("dns", "DNS query for '%s' queued\n", query_data
->hostname
);
725 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
730 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
737 dns_main_thread_cb(gpointer data
)
739 PurpleDnsQueryData
*query_data
= data
;
741 /* We're done, so purple_dnsquery_destroy() shouldn't think it is canceling an in-progress lookup */
742 query_data
->resolver
= NULL
;
744 if (query_data
->error_message
!= NULL
)
745 purple_dnsquery_failed(query_data
, query_data
->error_message
);
750 /* We don't want purple_dns_query_resolved() to free(hosts) */
751 hosts
= query_data
->hosts
;
752 query_data
->hosts
= NULL
;
753 purple_dnsquery_resolved(query_data
, hosts
);
760 dns_thread(gpointer data
)
762 PurpleDnsQueryData
*query_data
;
763 #ifdef HAVE_GETADDRINFO
765 struct addrinfo hints
, *res
, *tmp
;
768 struct sockaddr_in sin
;
776 if (!dns_str_is_ascii(query_data
->hostname
)) {
777 rc
= purple_network_convert_idn_to_ascii(query_data
->hostname
, &hostname
);
779 query_data
->error_message
= g_strdup_printf(_("Error converting %s "
780 "to punycode: %d"), query_data
->hostname
, rc
);
781 /* back to main thread */
782 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
785 } else /* intentional fallthru */
787 hostname
= g_strdup(query_data
->hostname
);
789 #ifdef HAVE_GETADDRINFO
790 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
791 memset(&hints
,0,sizeof(hints
));
794 * This is only used to convert a service
795 * name to a port number. As we know we are
796 * passing a number already, we know this
797 * value will not be really used by the C
800 hints
.ai_socktype
= SOCK_STREAM
;
802 hints
.ai_flags
|= AI_ADDRCONFIG
;
803 #endif /* AI_ADDRCONFIG */
804 if ((rc
= getaddrinfo(hostname
, servname
, &hints
, &res
)) == 0) {
807 query_data
->hosts
= g_slist_append(query_data
->hosts
,
808 GSIZE_TO_POINTER(res
->ai_addrlen
));
809 query_data
->hosts
= g_slist_append(query_data
->hosts
,
810 g_memdup(res
->ai_addr
, res
->ai_addrlen
));
815 query_data
->error_message
= g_strdup_printf(_("Error resolving %s:\n%s"), query_data
->hostname
, purple_gai_strerror(rc
));
818 if ((hp
= gethostbyname(hostname
))) {
819 memset(&sin
, 0, sizeof(struct sockaddr_in
));
820 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
821 sin
.sin_family
= hp
->h_addrtype
;
822 sin
.sin_port
= htons(query_data
->port
);
824 query_data
->hosts
= g_slist_append(query_data
->hosts
,
825 GSIZE_TO_POINTER(sizeof(sin
)));
826 query_data
->hosts
= g_slist_append(query_data
->hosts
,
827 g_memdup(&sin
, sizeof(sin
)));
829 query_data
->error_message
= g_strdup_printf(_("Error resolving %s: %d"), query_data
->hostname
, h_errno
);
834 /* back to main thread */
835 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
841 resolve_host(gpointer data
)
843 PurpleDnsQueryData
*query_data
;
847 query_data
->timeout
= 0;
849 if (purple_dnsquery_ui_resolve(query_data
))
851 /* The UI is handling the resolve; we're done */
855 if (!resolve_ip(query_data
))
858 * Spin off a separate thread to perform the DNS lookup so
859 * that we don't block the UI.
861 query_data
->resolver
= g_thread_create(dns_thread
,
862 query_data
, FALSE
, &err
);
863 if (query_data
->resolver
== NULL
)
866 g_snprintf(message
, sizeof(message
), _("Thread creation failure: %s"),
867 (err
&& err
->message
) ? err
->message
: _("Unknown reason"));
869 purple_dnsquery_failed(query_data
, message
);
877 purple_dnsquery_a(const char *hostname
, int port
,
878 PurpleDnsQueryConnectFunction callback
, gpointer data
)
880 PurpleDnsQueryData
*query_data
;
882 g_return_val_if_fail(hostname
!= NULL
, NULL
);
883 g_return_val_if_fail(port
!= 0, NULL
);
884 g_return_val_if_fail(callback
!= NULL
, NULL
);
886 purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname
);
888 query_data
= g_new0(PurpleDnsQueryData
, 1);
889 query_data
->hostname
= g_strdup(hostname
);
890 g_strstrip(query_data
->hostname
);
891 query_data
->port
= port
;
892 query_data
->callback
= callback
;
893 query_data
->data
= data
;
895 if (strlen(query_data
->hostname
) == 0)
897 purple_dnsquery_destroy(query_data
);
898 g_return_val_if_reached(NULL
);
901 /* Don't call the callback before returning */
902 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
907 #else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
910 * We weren't able to do anything fancier above, so use the
911 * fail-safe name resolution code, which is blocking.
915 resolve_host(gpointer data
)
917 PurpleDnsQueryData
*query_data
;
918 struct sockaddr_in sin
;
919 GSList
*hosts
= NULL
;
922 query_data
->timeout
= 0;
924 if (purple_dnsquery_ui_resolve(query_data
))
926 /* The UI is handling the resolve; we're done */
930 if (!inet_aton(query_data
->hostname
, &sin
.sin_addr
)) {
934 if (!dns_str_is_ascii(query_data
->hostname
)) {
935 int ret
= purple_network_convert_idn_to_ascii(query_data
->hostname
,
939 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
940 query_data
->hostname
, ret
);
941 purple_dnsquery_failed(query_data
, message
);
944 } else /* fallthrough is intentional to the g_strdup */
946 hostname
= g_strdup(query_data
->hostname
);
948 if(!(hp
= gethostbyname(hostname
))) {
950 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
951 query_data
->hostname
, h_errno
);
952 purple_dnsquery_failed(query_data
, message
);
956 memset(&sin
, 0, sizeof(struct sockaddr_in
));
957 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
958 sin
.sin_family
= hp
->h_addrtype
;
961 sin
.sin_family
= AF_INET
;
962 sin
.sin_port
= htons(query_data
->port
);
964 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
965 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
967 purple_dnsquery_resolved(query_data
, hosts
);
973 purple_dnsquery_a(const char *hostname
, int port
,
974 PurpleDnsQueryConnectFunction callback
, gpointer data
)
976 PurpleDnsQueryData
*query_data
;
978 g_return_val_if_fail(hostname
!= NULL
, NULL
);
979 g_return_val_if_fail(port
!= 0, NULL
);
980 g_return_val_if_fail(callback
!= NULL
, NULL
);
982 query_data
= g_new(PurpleDnsQueryData
, 1);
983 query_data
->hostname
= g_strdup(hostname
);
984 g_strstrip(query_data
->hostname
);
985 query_data
->port
= port
;
986 query_data
->callback
= callback
;
987 query_data
->data
= data
;
989 if (strlen(query_data
->hostname
) == 0)
991 purple_dnsquery_destroy(query_data
);
992 g_return_val_if_reached(NULL
);
995 /* Don't call the callback before returning */
996 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
1001 #endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
1004 purple_dnsquery_destroy(PurpleDnsQueryData
*query_data
)
1006 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
1008 if (ops
&& ops
->destroy
)
1009 ops
->destroy(query_data
);
1011 #if defined(PURPLE_DNSQUERY_USE_FORK)
1012 queued_requests
= g_slist_remove(queued_requests
, query_data
);
1014 if (query_data
->resolver
!= NULL
)
1016 * This is only non-NULL when we're cancelling an in-progress
1017 * query. Ideally we would tell our resolver child to stop
1018 * resolving shit and then we would add it back to the
1019 * free_dns_children linked list. However, it's hard to tell
1020 * children stuff, they just don't listen. So we'll just
1021 * kill the process and allow a new child to be started if we
1022 * have more stuff to resolve.
1024 purple_dnsquery_resolver_destroy(query_data
->resolver
);
1025 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
1026 if (query_data
->resolver
!= NULL
)
1029 * It's not really possible to kill a thread. So instead we
1030 * just set the callback to NULL and let the DNS lookup
1033 query_data
->callback
= NULL
;
1037 while (query_data
->hosts
!= NULL
)
1039 /* Discard the length... */
1040 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
1041 /* Free the address... */
1042 g_free(query_data
->hosts
->data
);
1043 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
1045 g_free(query_data
->error_message
);
1046 #endif /* end _WIN32 */
1048 if (query_data
->timeout
> 0)
1049 purple_timeout_remove(query_data
->timeout
);
1051 g_free(query_data
->hostname
);
1056 purple_dnsquery_get_host(PurpleDnsQueryData
*query_data
)
1058 g_return_val_if_fail(query_data
!= NULL
, NULL
);
1060 return query_data
->hostname
;
1064 purple_dnsquery_get_port(PurpleDnsQueryData
*query_data
)
1066 g_return_val_if_fail(query_data
!= NULL
, 0);
1068 return query_data
->port
;
1072 purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps
*ops
)
1074 dns_query_ui_ops
= ops
;
1077 PurpleDnsQueryUiOps
*
1078 purple_dnsquery_get_ui_ops(void)
1080 /* It is perfectly acceptable for dns_query_ui_ops to be NULL; this just
1081 * means that the default platform-specific implementation will be used.
1083 return dns_query_ui_ops
;
1087 purple_dnsquery_init(void)
1092 purple_dnsquery_uninit(void)
1094 #if defined(PURPLE_DNSQUERY_USE_FORK)
1095 while (free_dns_children
!= NULL
)
1097 purple_dnsquery_resolver_destroy(free_dns_children
->data
);
1098 free_dns_children
= g_slist_remove(free_dns_children
, free_dns_children
->data
);
1100 #endif /* end PURPLE_DNSQUERY_USE_FORK */