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 #define MAX_ADDR_RESPONSE_LEN 1048576
43 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
44 #define PURPLE_DNSQUERY_USE_FORK
46 /**************************************************************************
48 **************************************************************************/
50 static PurpleDnsQueryUiOps
*dns_query_ui_ops
= NULL
;
52 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess
;
54 struct _PurpleDnsQueryData
{
57 PurpleDnsQueryConnectFunction callback
;
60 PurpleAccount
*account
;
62 #if defined(PURPLE_DNSQUERY_USE_FORK)
63 PurpleDnsQueryResolverProcess
*resolver
;
64 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
71 #if defined(PURPLE_DNSQUERY_USE_FORK)
73 #define MAX_DNS_CHILDREN 4
76 * This structure keeps a reference to a child resolver process.
78 struct _PurpleDnsQueryResolverProcess
{
84 static GSList
*free_dns_children
= NULL
;
85 /* TODO: Make me a GQueue when we require >= glib 2.4 */
86 static GSList
*queued_requests
= NULL
;
88 static int number_of_dns_children
= 0;
91 * This is a convenience struct used to pass data to
92 * the child resolver process.
98 #endif /* end PURPLE_DNSQUERY_USE_FORK */
101 purple_dnsquery_resolved(PurpleDnsQueryData
*query_data
, GSList
*hosts
)
103 purple_debug_info("dnsquery", "IP resolved for %s\n", query_data
->hostname
);
104 if (query_data
->callback
!= NULL
)
105 query_data
->callback(hosts
, query_data
->data
, NULL
);
109 * Callback is a required parameter, but it can get set to
110 * NULL if we cancel a thread-based DNS lookup. So we need
113 while (hosts
!= NULL
)
115 hosts
= g_slist_remove(hosts
, hosts
->data
);
117 hosts
= g_slist_remove(hosts
, hosts
->data
);
121 #ifdef PURPLE_DNSQUERY_USE_FORK
123 * Add the resolver to the list of available resolvers, and set it
124 * to NULL so that it doesn't get destroyed along with the query_data
126 if (query_data
->resolver
)
128 free_dns_children
= g_slist_prepend(free_dns_children
, query_data
->resolver
);
129 query_data
->resolver
= NULL
;
131 #endif /* PURPLE_DNSQUERY_USE_FORK */
133 purple_dnsquery_destroy(query_data
);
137 purple_dnsquery_failed(PurpleDnsQueryData
*query_data
, const gchar
*error_message
)
139 purple_debug_error("dnsquery", "%s\n", error_message
);
140 if (query_data
->callback
!= NULL
)
141 query_data
->callback(NULL
, query_data
->data
, error_message
);
142 purple_dnsquery_destroy(query_data
);
146 purple_dnsquery_ui_resolve(PurpleDnsQueryData
*query_data
)
148 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
150 if (ops
&& ops
->resolve_host
)
151 return ops
->resolve_host(query_data
, purple_dnsquery_resolved
, purple_dnsquery_failed
);
157 resolve_ip(PurpleDnsQueryData
*query_data
)
159 #if defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST)
160 struct addrinfo hints
, *res
;
163 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
164 memset(&hints
, 0, sizeof(hints
));
165 hints
.ai_family
= AF_UNSPEC
;
166 hints
.ai_flags
|= AI_NUMERICHOST
;
168 if (0 == getaddrinfo(query_data
->hostname
, servname
, &hints
, &res
))
170 GSList
*hosts
= NULL
;
171 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(res
->ai_addrlen
));
172 hosts
= g_slist_append(hosts
, g_memdup(res
->ai_addr
, res
->ai_addrlen
));
173 purple_dnsquery_resolved(query_data
, hosts
);
178 #else /* defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) */
179 struct sockaddr_in sin
;
180 if (inet_aton(query_data
->hostname
, &sin
.sin_addr
))
183 * The given "hostname" is actually an IP address, so we
184 * don't need to do anything.
186 GSList
*hosts
= NULL
;
187 sin
.sin_family
= AF_INET
;
188 sin
.sin_port
= htons(query_data
->port
);
189 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
190 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
191 purple_dnsquery_resolved(query_data
, hosts
);
202 dns_str_is_ascii(const char *name
)
205 for (c
= (guchar
*)name
; c
&& *c
; ++c
) {
214 #if defined(PURPLE_DNSQUERY_USE_FORK)
221 * Begin the DNS resolver child process functions.
224 G_GNUC_NORETURN
static void
225 trap_gdb_bug(int sig
)
227 const char *message
=
228 "Purple's DNS child got a SIGTRAP signal.\n"
229 "This can be caused by trying to run purple inside gdb.\n"
230 "There is a known gdb bug which prevents this. Supposedly purple\n"
231 "should have detected you were using gdb and used an ugly hack,\n"
232 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n"
233 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
234 fputs("\n* * *\n",stderr
);
235 fputs(message
,stderr
);
236 fputs("* * *\n\n",stderr
);
237 execlp("xmessage","xmessage","-center", message
, NULL
);
243 write_to_parent(int fd
, const void *buf
, size_t count
)
247 written
= write(fd
, buf
, count
);
248 if (written
< 0 || (gsize
)written
!= count
) {
250 fprintf(stderr
, "dns[%d]: Error writing data to "
251 "parent: %s\n", getpid(), strerror(errno
));
253 fprintf(stderr
, "dns[%d]: Error: Tried to write %"
254 G_GSIZE_FORMAT
" bytes to parent but instead "
255 "wrote %" G_GSIZE_FORMAT
" bytes\n",
256 getpid(), count
, written
);
260 G_GNUC_NORETURN
static void
261 purple_dnsquery_resolver_run(int child_out
, int child_in
, gboolean show_debug
)
263 dns_params_t dns_params
;
264 const size_t zero
= 0;
266 #ifdef HAVE_GETADDRINFO
267 struct addrinfo hints
, *res
, *tmp
;
270 struct sockaddr_in sin
;
271 const size_t addrlen
= sizeof(sin
);
276 purple_restore_default_signal_handlers();
277 signal(SIGTRAP
, trap_gdb_bug
);
281 * We resolve 1 host name for each iteration of this
284 * The top half of this reads in the hostname and port
285 * number from the socket with our parent. The bottom
286 * half of this resolves the IP (blocking) and sends
287 * the result back to our parent, when finished.
291 struct timeval tv
= { .tv_sec
= 20, .tv_usec
= 0 };
293 FD_SET(child_in
, &fds
);
294 rc
= select(child_in
+ 1, &fds
, NULL
, NULL
, &tv
);
297 printf("dns[%d]: nobody needs me... =(\n", getpid());
300 rc
= read(child_in
, &dns_params
, sizeof(dns_params_t
));
302 fprintf(stderr
, "dns[%d]: Error: Could not read dns_params: "
303 "%s\n", getpid(), strerror(errno
));
308 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
311 if (dns_params
.hostname
[0] == '\0') {
312 fprintf(stderr
, "dns[%d]: Error: Parent requested resolution "
313 "of an empty hostname (port = %d)!!!\n", getpid(),
319 if (!dns_str_is_ascii(dns_params
.hostname
)) {
320 rc
= purple_network_convert_idn_to_ascii(dns_params
.hostname
, &hostname
);
322 write_to_parent(child_out
, &rc
, sizeof(rc
));
324 fprintf(stderr
, "dns[%d] Error: IDN conversion returned "
325 "%d\n", getpid(), rc
);
326 dns_params
.hostname
[0] = '\0';
329 } else /* intentional to execute the g_strdup */
331 hostname
= g_strdup(dns_params
.hostname
);
333 /* We have the hostname and port, now resolve the IP */
335 #ifdef HAVE_GETADDRINFO
336 g_snprintf(servname
, sizeof(servname
), "%d", dns_params
.port
);
337 memset(&hints
, 0, sizeof(hints
));
339 /* This is only used to convert a service
340 * name to a port number. As we know we are
341 * passing a number already, we know this
342 * value will not be really used by the C
345 hints
.ai_socktype
= SOCK_STREAM
;
347 hints
.ai_flags
|= AI_ADDRCONFIG
;
348 #endif /* AI_ADDRCONFIG */
349 rc
= getaddrinfo(hostname
, servname
, &hints
, &res
);
350 write_to_parent(child_out
, &rc
, sizeof(rc
));
353 printf("dns[%d] Error: getaddrinfo returned %d\n",
355 dns_params
.hostname
[0] = '\0';
362 size_t ai_addrlen
= res
->ai_addrlen
;
363 write_to_parent(child_out
, &ai_addrlen
, sizeof(ai_addrlen
));
364 write_to_parent(child_out
, res
->ai_addr
, res
->ai_addrlen
);
370 if (!(hp
= gethostbyname(hostname
))) {
371 write_to_parent(child_out
, &h_errno
, sizeof(int));
374 printf("DNS Error: %d\n", h_errno
);
377 memset(&sin
, 0, sizeof(struct sockaddr_in
));
378 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
379 sin
.sin_family
= hp
->h_addrtype
;
381 sin
.sin_port
= htons(dns_params
.port
);
383 write_to_parent(child_out
, &rc
, sizeof(rc
));
384 write_to_parent(child_out
, &addrlen
, sizeof(addrlen
));
385 write_to_parent(child_out
, &sin
, addrlen
);
387 write_to_parent(child_out
, &zero
, sizeof(zero
));
388 dns_params
.hostname
[0] = '\0';
400 * End the DNS resolver child process functions.
404 * Begin the functions for dealing with the DNS child processes.
407 cope_with_gdb_brokenness(void)
410 static gboolean already_done
= FALSE
;
419 g_snprintf(s
, sizeof(s
), "/proc/%d/exe", ppid
);
420 n
= readlink(s
, e
, sizeof(e
));
424 e
[MIN((gsize
)n
,sizeof(e
)-1)] = '\0';
426 if(strstr(e
,"gdb")) {
427 purple_debug_info("dns",
428 "Debugger detected, performing useless query...\n");
429 gethostbyname("x.x.x.x.x");
435 purple_dnsquery_resolver_destroy(PurpleDnsQueryResolverProcess
*resolver
)
437 g_return_if_fail(resolver
!= NULL
);
439 /* Keep this before the kill() call below. */
440 if (resolver
->inpa
!= 0) {
441 purple_input_remove(resolver
->inpa
);
446 * We might as well attempt to kill our child process. It really
447 * doesn't matter if this fails, because children will expire on
448 * their own after a few seconds.
450 if (resolver
->dns_pid
> 0)
451 kill(resolver
->dns_pid
, SIGKILL
);
453 close(resolver
->fd_in
);
454 close(resolver
->fd_out
);
458 number_of_dns_children
--;
461 static PurpleDnsQueryResolverProcess
*
462 purple_dnsquery_resolver_new(gboolean show_debug
)
464 PurpleDnsQueryResolverProcess
*resolver
;
465 int child_out
[2], child_in
[2];
467 /* Create pipes for communicating with the child process */
468 if (pipe(child_out
) || pipe(child_in
)) {
469 purple_debug_error("dns",
470 "Could not create pipes: %s\n", g_strerror(errno
));
474 resolver
= g_new(PurpleDnsQueryResolverProcess
, 1);
477 cope_with_gdb_brokenness();
479 /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */
480 resolver
->dns_pid
= fork();
482 /* If we are the child process... */
483 if (resolver
->dns_pid
== 0) {
484 /* We should not access the parent's side of the pipes, so close them */
488 purple_dnsquery_resolver_run(child_out
[1], child_in
[0], show_debug
);
489 /* The thread calls _exit() rather than returning, so we never get here */
492 /* We should not access the child's side of the pipes, so close them */
495 if (resolver
->dns_pid
== -1) {
496 purple_debug_error("dns",
497 "Could not create child process for DNS: %s\n",
499 purple_dnsquery_resolver_destroy(resolver
);
503 resolver
->fd_out
= child_out
[0];
504 resolver
->fd_in
= child_in
[1];
505 number_of_dns_children
++;
506 purple_debug_info("dns",
507 "Created new DNS child %d, there are now %d children.\n",
508 resolver
->dns_pid
, number_of_dns_children
);
514 * @return TRUE if the request was sent succesfully. FALSE
515 * if the request could not be sent. This isn't
516 * necessarily an error. If the child has expired,
517 * for example, we won't be able to send the message.
520 send_dns_request_to_child(PurpleDnsQueryData
*query_data
,
521 PurpleDnsQueryResolverProcess
*resolver
)
524 dns_params_t dns_params
;
527 /* This waitpid might return the child's PID if it has recently
528 * exited, or it might return an error if it exited "long
529 * enough" ago that it has already been reaped; in either
530 * instance, we can't use it. */
531 pid
= waitpid(resolver
->dns_pid
, NULL
, WNOHANG
);
533 purple_debug_warning("dns", "DNS child %d no longer exists\n",
535 purple_dnsquery_resolver_destroy(resolver
);
537 } else if (pid
< 0) {
538 purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n",
539 resolver
->dns_pid
, g_strerror(errno
));
540 purple_dnsquery_resolver_destroy(resolver
);
544 /* Copy the hostname and port into a single data structure */
545 strncpy(dns_params
.hostname
, query_data
->hostname
, sizeof(dns_params
.hostname
) - 1);
546 dns_params
.hostname
[sizeof(dns_params
.hostname
) - 1] = '\0';
547 dns_params
.port
= query_data
->port
;
549 /* Send the data structure to the child */
550 rc
= write(resolver
->fd_in
, &dns_params
, sizeof(dns_params
));
552 purple_debug_error("dns", "Unable to write to DNS child %d: %s\n",
553 resolver
->dns_pid
, g_strerror(errno
));
554 purple_dnsquery_resolver_destroy(resolver
);
557 if ((gsize
)rc
< sizeof(dns_params
)) {
558 purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
559 " bytes to child but only wrote %" G_GSSIZE_FORMAT
"\n",
560 sizeof(dns_params
), rc
);
561 purple_dnsquery_resolver_destroy(resolver
);
565 purple_debug_info("dns",
566 "Successfully sent DNS request to child %d\n",
569 query_data
->resolver
= resolver
;
574 static void host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
);
577 handle_next_queued_request(void)
579 PurpleDnsQueryData
*query_data
;
580 PurpleDnsQueryResolverProcess
*resolver
;
582 if (queued_requests
== NULL
)
583 /* No more DNS queries, yay! */
586 query_data
= queued_requests
->data
;
587 queued_requests
= g_slist_delete_link(queued_requests
, queued_requests
);
590 * If we have any children, attempt to have them perform the DNS
591 * query. If we're able to send the query then resolver will be
592 * set to the PurpleDnsQueryResolverProcess. Otherwise, resolver
593 * will be NULL and we'll need to create a new DNS request child.
595 while (free_dns_children
!= NULL
)
597 resolver
= free_dns_children
->data
;
598 free_dns_children
= g_slist_remove(free_dns_children
, resolver
);
600 if (send_dns_request_to_child(query_data
, resolver
))
601 /* We found an acceptable child, yay */
605 /* We need to create a new DNS request child */
606 if (query_data
->resolver
== NULL
)
608 if (number_of_dns_children
>= MAX_DNS_CHILDREN
)
610 /* Apparently all our children are busy */
611 queued_requests
= g_slist_prepend(queued_requests
, query_data
);
615 resolver
= purple_dnsquery_resolver_new(purple_debug_is_enabled());
616 if (resolver
== NULL
)
618 purple_dnsquery_failed(query_data
, _("Unable to create new resolver process\n"));
621 if (!send_dns_request_to_child(query_data
, resolver
))
623 purple_dnsquery_failed(query_data
, _("Unable to send request to resolver process\n"));
628 query_data
->resolver
->inpa
= purple_input_add(query_data
->resolver
->fd_out
,
629 PURPLE_INPUT_READ
, host_resolved
, query_data
);
633 * End the functions for dealing with the DNS child processes.
637 host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
)
639 PurpleDnsQueryData
*query_data
;
641 GSList
*hosts
= NULL
;
642 struct sockaddr
*addr
= NULL
;
648 purple_debug_info("dns", "Got response for '%s'\n", query_data
->hostname
);
649 purple_input_remove(query_data
->resolver
->inpa
);
650 query_data
->resolver
->inpa
= 0;
652 rc
= read(query_data
->resolver
->fd_out
, &err
, sizeof(err
));
653 if ((rc
== 4) && (err
!= 0))
655 #ifdef HAVE_GETADDRINFO
656 g_snprintf(message
, sizeof(message
), _("Error resolving %s:\n%s"),
657 query_data
->hostname
, purple_gai_strerror(err
));
659 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
660 query_data
->hostname
, err
);
662 /* Re-read resolv.conf and friends in case DNS servers have changed */
665 purple_dnsquery_failed(query_data
, message
);
669 rc
= read(query_data
->resolver
->fd_out
, &addrlen
, sizeof(addrlen
));
670 if (rc
> 0 && addrlen
> 0 && addrlen
< MAX_ADDR_RESPONSE_LEN
) {
671 addr
= g_malloc(addrlen
);
672 rc
= read(query_data
->resolver
->fd_out
, addr
, addrlen
);
673 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(addrlen
));
674 hosts
= g_slist_append(hosts
, addr
);
679 /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */
680 purple_dnsquery_resolved(query_data
, hosts
);
682 } else if (rc
== -1) {
683 g_snprintf(message
, sizeof(message
), _("Error reading from resolver process:\n%s"), g_strerror(errno
));
684 purple_dnsquery_failed(query_data
, message
);
686 } else if (rc
== 0) {
687 g_snprintf(message
, sizeof(message
), _("Resolver process exited without answering our request"));
688 purple_dnsquery_failed(query_data
, message
);
691 handle_next_queued_request();
695 resolve_host(PurpleDnsQueryData
*query_data
)
697 queued_requests
= g_slist_append(queued_requests
, query_data
);
699 handle_next_queued_request();
702 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
709 dns_main_thread_cb(gpointer data
)
711 PurpleDnsQueryData
*query_data
= data
;
713 /* We're done, so purple_dnsquery_destroy() shouldn't think it is canceling an in-progress lookup */
714 query_data
->resolver
= NULL
;
716 if (query_data
->error_message
!= NULL
)
717 purple_dnsquery_failed(query_data
, query_data
->error_message
);
722 /* We don't want purple_dns_query_resolved() to free(hosts) */
723 hosts
= query_data
->hosts
;
724 query_data
->hosts
= NULL
;
725 purple_dnsquery_resolved(query_data
, hosts
);
732 dns_thread(gpointer data
)
734 PurpleDnsQueryData
*query_data
;
735 #ifdef HAVE_GETADDRINFO
737 struct addrinfo hints
, *res
, *tmp
;
740 struct sockaddr_in sin
;
748 if (!dns_str_is_ascii(query_data
->hostname
)) {
749 rc
= purple_network_convert_idn_to_ascii(query_data
->hostname
, &hostname
);
751 query_data
->error_message
= g_strdup_printf(_("Error converting %s "
752 "to punycode: %d"), query_data
->hostname
, rc
);
753 /* back to main thread */
754 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
757 } else /* intentional fallthru */
759 hostname
= g_strdup(query_data
->hostname
);
761 #ifdef HAVE_GETADDRINFO
762 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
763 memset(&hints
,0,sizeof(hints
));
766 * This is only used to convert a service
767 * name to a port number. As we know we are
768 * passing a number already, we know this
769 * value will not be really used by the C
772 hints
.ai_socktype
= SOCK_STREAM
;
774 hints
.ai_flags
|= AI_ADDRCONFIG
;
775 #endif /* AI_ADDRCONFIG */
776 if ((rc
= getaddrinfo(hostname
, servname
, &hints
, &res
)) == 0) {
779 query_data
->hosts
= g_slist_append(query_data
->hosts
,
780 GSIZE_TO_POINTER(res
->ai_addrlen
));
781 query_data
->hosts
= g_slist_append(query_data
->hosts
,
782 g_memdup(res
->ai_addr
, res
->ai_addrlen
));
787 query_data
->error_message
= g_strdup_printf(_("Error resolving %s:\n%s"), query_data
->hostname
, purple_gai_strerror(rc
));
790 if ((hp
= gethostbyname(hostname
))) {
791 memset(&sin
, 0, sizeof(struct sockaddr_in
));
792 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
793 sin
.sin_family
= hp
->h_addrtype
;
794 sin
.sin_port
= htons(query_data
->port
);
796 query_data
->hosts
= g_slist_append(query_data
->hosts
,
797 GSIZE_TO_POINTER(sizeof(sin
)));
798 query_data
->hosts
= g_slist_append(query_data
->hosts
,
799 g_memdup(&sin
, sizeof(sin
)));
801 query_data
->error_message
= g_strdup_printf(_("Error resolving %s: %d"), query_data
->hostname
, h_errno
);
806 /* back to main thread */
807 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
813 resolve_host(PurpleDnsQueryData
*query_data
)
818 * Spin off a separate thread to perform the DNS lookup so
819 * that we don't block the UI.
821 query_data
->resolver
= g_thread_create(dns_thread
,
822 query_data
, FALSE
, &err
);
823 if (query_data
->resolver
== NULL
)
826 g_snprintf(message
, sizeof(message
), _("Thread creation failure: %s"),
827 (err
&& err
->message
) ? err
->message
: _("Unknown reason"));
829 purple_dnsquery_failed(query_data
, message
);
833 #else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
836 * We weren't able to do anything fancier above, so use the
837 * fail-safe name resolution code, which is blocking.
841 resolve_host(PurpleDnsQueryData
*query_data
)
843 struct sockaddr_in sin
;
844 GSList
*hosts
= NULL
;
848 if (!dns_str_is_ascii(query_data
->hostname
)) {
849 int ret
= purple_network_convert_idn_to_ascii(query_data
->hostname
,
853 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
854 query_data
->hostname
, ret
);
855 purple_dnsquery_failed(query_data
, message
);
858 } else /* fallthrough is intentional to the g_strdup */
860 hostname
= g_strdup(query_data
->hostname
);
862 if(!(hp
= gethostbyname(hostname
))) {
864 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
865 query_data
->hostname
, h_errno
);
866 purple_dnsquery_failed(query_data
, message
);
870 memset(&sin
, 0, sizeof(struct sockaddr_in
));
871 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
872 sin
.sin_family
= hp
->h_addrtype
;
874 sin
.sin_port
= htons(query_data
->port
);
876 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
877 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
879 purple_dnsquery_resolved(query_data
, hosts
);
882 #endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
885 initiate_resolving(gpointer data
)
887 PurpleDnsQueryData
*query_data
;
888 PurpleProxyType proxy_type
;
891 query_data
->timeout
= 0;
893 if (resolve_ip(query_data
))
894 /* resolve_ip calls purple_dnsquery_resolved */
897 proxy_type
= purple_proxy_info_get_type(
898 purple_proxy_get_setup(query_data
->account
));
899 if (proxy_type
== PURPLE_PROXY_TOR
) {
900 purple_dnsquery_failed(query_data
,
901 _("Aborting DNS lookup in Tor Proxy mode."));
905 if (purple_dnsquery_ui_resolve(query_data
))
906 /* The UI is handling the resolve; we're done */
909 resolve_host(query_data
);
915 purple_dnsquery_a_account(PurpleAccount
*account
, const char *hostname
, int port
,
916 PurpleDnsQueryConnectFunction callback
, gpointer data
)
918 PurpleDnsQueryData
*query_data
;
920 g_return_val_if_fail(hostname
!= NULL
, NULL
);
921 g_return_val_if_fail(port
!= 0, NULL
);
922 g_return_val_if_fail(callback
!= NULL
, NULL
);
924 purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname
);
926 query_data
= g_new0(PurpleDnsQueryData
, 1);
927 query_data
->hostname
= g_strdup(hostname
);
928 g_strstrip(query_data
->hostname
);
929 query_data
->port
= port
;
930 query_data
->callback
= callback
;
931 query_data
->data
= data
;
932 query_data
->account
= account
;
934 if (*query_data
->hostname
== '\0')
936 purple_dnsquery_destroy(query_data
);
937 g_return_val_if_reached(NULL
);
940 query_data
->timeout
= purple_timeout_add(0, initiate_resolving
, query_data
);
946 purple_dnsquery_a(const char *hostname
, int port
,
947 PurpleDnsQueryConnectFunction callback
, gpointer data
)
949 return purple_dnsquery_a_account(NULL
, hostname
, port
, callback
, data
);
953 purple_dnsquery_destroy(PurpleDnsQueryData
*query_data
)
955 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
957 if (ops
&& ops
->destroy
)
958 ops
->destroy(query_data
);
960 #if defined(PURPLE_DNSQUERY_USE_FORK)
961 queued_requests
= g_slist_remove(queued_requests
, query_data
);
963 if (query_data
->resolver
!= NULL
)
965 * This is only non-NULL when we're cancelling an in-progress
966 * query. Ideally we would tell our resolver child to stop
967 * resolving shit and then we would add it back to the
968 * free_dns_children linked list. However, it's hard to tell
969 * children stuff, they just don't listen. So we'll just
970 * kill the process and allow a new child to be started if we
971 * have more stuff to resolve.
973 purple_dnsquery_resolver_destroy(query_data
->resolver
);
974 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
975 if (query_data
->resolver
!= NULL
)
978 * It's not really possible to kill a thread. So instead we
979 * just set the callback to NULL and let the DNS lookup
982 query_data
->callback
= NULL
;
986 while (query_data
->hosts
!= NULL
)
988 /* Discard the length... */
989 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
990 /* Free the address... */
991 g_free(query_data
->hosts
->data
);
992 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
994 g_free(query_data
->error_message
);
995 #endif /* end _WIN32 */
997 if (query_data
->timeout
> 0)
998 purple_timeout_remove(query_data
->timeout
);
1000 g_free(query_data
->hostname
);
1005 purple_dnsquery_get_host(PurpleDnsQueryData
*query_data
)
1007 g_return_val_if_fail(query_data
!= NULL
, NULL
);
1009 return query_data
->hostname
;
1013 purple_dnsquery_get_port(PurpleDnsQueryData
*query_data
)
1015 g_return_val_if_fail(query_data
!= NULL
, 0);
1017 return query_data
->port
;
1021 purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps
*ops
)
1023 dns_query_ui_ops
= ops
;
1026 PurpleDnsQueryUiOps
*
1027 purple_dnsquery_get_ui_ops(void)
1029 /* It is perfectly acceptable for dns_query_ui_ops to be NULL; this just
1030 * means that the default platform-specific implementation will be used.
1032 return dns_query_ui_ops
;
1036 purple_dnsquery_init(void)
1041 purple_dnsquery_uninit(void)
1043 #if defined(PURPLE_DNSQUERY_USE_FORK)
1044 while (free_dns_children
!= NULL
)
1046 purple_dnsquery_resolver_destroy(free_dns_children
->data
);
1047 free_dns_children
= g_slist_remove(free_dns_children
, free_dns_children
->data
);
1049 #endif /* end PURPLE_DNSQUERY_USE_FORK */