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
35 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
36 #define PURPLE_DNSQUERY_USE_FORK
38 /**************************************************************************
40 **************************************************************************/
42 static PurpleDnsQueryUiOps
*dns_query_ui_ops
= NULL
;
44 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess
;
46 struct _PurpleDnsQueryData
{
49 PurpleDnsQueryConnectFunction callback
;
53 #if defined(PURPLE_DNSQUERY_USE_FORK)
54 PurpleDnsQueryResolverProcess
*resolver
;
55 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
62 #if defined(PURPLE_DNSQUERY_USE_FORK)
64 #define MAX_DNS_CHILDREN 4
67 * This structure keeps a reference to a child resolver process.
69 struct _PurpleDnsQueryResolverProcess
{
75 static GSList
*free_dns_children
= NULL
;
76 static GSList
*queued_requests
= NULL
;
78 static int number_of_dns_children
= 0;
81 * This is a convenience struct used to pass data to
82 * the child resolver process.
88 #endif /* end PURPLE_DNSQUERY_USE_FORK */
91 purple_dnsquery_resolved(PurpleDnsQueryData
*query_data
, GSList
*hosts
)
93 purple_debug_info("dnsquery", "IP resolved for %s\n", query_data
->hostname
);
94 if (query_data
->callback
!= NULL
)
95 query_data
->callback(hosts
, query_data
->data
, NULL
);
99 * Callback is a required parameter, but it can get set to
100 * NULL if we cancel a thread-based DNS lookup. So we need
103 while (hosts
!= NULL
)
105 hosts
= g_slist_remove(hosts
, hosts
->data
);
107 hosts
= g_slist_remove(hosts
, hosts
->data
);
111 #ifdef PURPLE_DNSQUERY_USE_FORK
113 * Add the resolver to the list of available resolvers, and set it
114 * to NULL so that it doesn't get destroyed along with the query_data
116 free_dns_children
= g_slist_prepend(free_dns_children
, query_data
->resolver
);
117 query_data
->resolver
= NULL
;
118 #endif /* PURPLE_DNSQUERY_USE_FORK */
120 purple_dnsquery_destroy(query_data
);
124 purple_dnsquery_failed(PurpleDnsQueryData
*query_data
, const gchar
*error_message
)
126 purple_debug_info("dnsquery", "%s\n", error_message
);
127 if (query_data
->callback
!= NULL
)
128 query_data
->callback(NULL
, query_data
->data
, error_message
);
129 purple_dnsquery_destroy(query_data
);
133 purple_dnsquery_ui_resolve(PurpleDnsQueryData
*query_data
)
135 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
137 if (ops
&& ops
->resolve_host
)
138 return ops
->resolve_host(query_data
, purple_dnsquery_resolved
, purple_dnsquery_failed
);
143 #if defined(PURPLE_DNSQUERY_USE_FORK)
150 * Begin the DNS resolver child process functions.
153 G_GNUC_NORETURN
static void
154 trap_gdb_bug(int sig
)
156 const char *message
=
157 "Purple's DNS child got a SIGTRAP signal.\n"
158 "This can be caused by trying to run purple inside gdb.\n"
159 "There is a known gdb bug which prevents this. Supposedly purple\n"
160 "should have detected you were using gdb and used an ugly hack,\n"
161 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n"
162 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
163 fputs("\n* * *\n",stderr
);
164 fputs(message
,stderr
);
165 fputs("* * *\n\n",stderr
);
166 execlp("xmessage","xmessage","-center", message
, NULL
);
172 write_to_parent(int fd
, const void *buf
, size_t count
)
176 written
= write(fd
, buf
, count
);
177 if (written
!= count
) {
179 fprintf(stderr
, "dns[%d]: Error writing data to "
180 "parent: %s\n", getpid(), strerror(errno
));
182 fprintf(stderr
, "dns[%d]: Error: Tried to write %"
183 G_GSIZE_FORMAT
" bytes to parent but instead "
184 "wrote %" G_GSIZE_FORMAT
" bytes\n",
185 getpid(), count
, written
);
189 G_GNUC_NORETURN
static void
190 purple_dnsquery_resolver_run(int child_out
, int child_in
, gboolean show_debug
)
192 dns_params_t dns_params
;
193 const size_t zero
= 0;
195 #ifdef HAVE_GETADDRINFO
196 struct addrinfo hints
, *res
, *tmp
;
199 struct sockaddr_in sin
;
200 const size_t addrlen
= sizeof(sin
);
204 purple_restore_default_signal_handlers();
205 signal(SIGTRAP
, trap_gdb_bug
);
209 * We resolve 1 host name for each iteration of this
212 * The top half of this reads in the hostname and port
213 * number from the socket with our parent. The bottom
214 * half of this resolves the IP (blocking) and sends
215 * the result back to our parent, when finished.
219 struct timeval tv
= { .tv_sec
= 20, .tv_usec
= 0 };
221 FD_SET(child_in
, &fds
);
222 rc
= select(child_in
+ 1, &fds
, NULL
, NULL
, &tv
);
225 printf("dns[%d]: nobody needs me... =(\n", getpid());
228 rc
= read(child_in
, &dns_params
, sizeof(dns_params_t
));
230 fprintf(stderr
, "dns[%d]: Error: Could not read dns_params: "
231 "%s\n", getpid(), strerror(errno
));
236 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
239 if (dns_params
.hostname
[0] == '\0') {
240 fprintf(stderr
, "dns[%d]: Error: Parent requested resolution "
241 "of an empty hostname (port = %d)!!!\n", getpid(),
246 /* We have the hostname and port, now resolve the IP */
248 #ifdef HAVE_GETADDRINFO
249 g_snprintf(servname
, sizeof(servname
), "%d", dns_params
.port
);
250 memset(&hints
, 0, sizeof(hints
));
252 /* This is only used to convert a service
253 * name to a port number. As we know we are
254 * passing a number already, we know this
255 * value will not be really used by the C
258 hints
.ai_socktype
= SOCK_STREAM
;
259 rc
= getaddrinfo(dns_params
.hostname
, servname
, &hints
, &res
);
260 write_to_parent(child_out
, &rc
, sizeof(rc
));
264 printf("dns[%d] Error: getaddrinfo returned %d\n",
266 dns_params
.hostname
[0] = '\0';
271 size_t ai_addrlen
= res
->ai_addrlen
;
272 write_to_parent(child_out
, &ai_addrlen
, sizeof(ai_addrlen
));
273 write_to_parent(child_out
, res
->ai_addr
, res
->ai_addrlen
);
278 if (!inet_aton(dns_params
.hostname
, &sin
.sin_addr
)) {
280 if (!(hp
= gethostbyname(dns_params
.hostname
))) {
281 write_to_parent(child_out
, &h_errno
, sizeof(int));
284 printf("DNS Error: %d\n", h_errno
);
287 memset(&sin
, 0, sizeof(struct sockaddr_in
));
288 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
289 sin
.sin_family
= hp
->h_addrtype
;
291 sin
.sin_family
= AF_INET
;
293 sin
.sin_port
= htons(dns_params
.port
);
295 write_to_parent(child_out
, &rc
, sizeof(rc
));
296 write_to_parent(child_out
, &addrlen
, sizeof(addrlen
));
297 write_to_parent(child_out
, &sin
, addrlen
);
299 write_to_parent(child_out
, &zero
, sizeof(zero
));
300 dns_params
.hostname
[0] = '\0';
309 * End the DNS resolver child process functions.
313 * Begin the functions for dealing with the DNS child processes.
316 cope_with_gdb_brokenness(void)
319 static gboolean already_done
= FALSE
;
328 snprintf(s
, sizeof(s
), "/proc/%d/exe", ppid
);
329 n
= readlink(s
, e
, sizeof(e
));
333 e
[MIN(n
,sizeof(e
)-1)] = '\0';
335 if(strstr(e
,"gdb")) {
336 purple_debug_info("dns",
337 "Debugger detected, performing useless query...\n");
338 gethostbyname("x.x.x.x.x");
344 purple_dnsquery_resolver_destroy(PurpleDnsQueryResolverProcess
*resolver
)
346 g_return_if_fail(resolver
!= NULL
);
348 /* Keep this before the kill() call below. */
349 if (resolver
->inpa
!= 0) {
350 purple_input_remove(resolver
->inpa
);
355 * We might as well attempt to kill our child process. It really
356 * doesn't matter if this fails, because children will expire on
357 * their own after a few seconds.
359 if (resolver
->dns_pid
> 0)
360 kill(resolver
->dns_pid
, SIGKILL
);
362 close(resolver
->fd_in
);
363 close(resolver
->fd_out
);
367 number_of_dns_children
--;
370 static PurpleDnsQueryResolverProcess
*
371 purple_dnsquery_resolver_new(gboolean show_debug
)
373 PurpleDnsQueryResolverProcess
*resolver
;
374 int child_out
[2], child_in
[2];
376 /* Create pipes for communicating with the child process */
377 if (pipe(child_out
) || pipe(child_in
)) {
378 purple_debug_error("dns",
379 "Could not create pipes: %s\n", g_strerror(errno
));
383 resolver
= g_new(PurpleDnsQueryResolverProcess
, 1);
386 cope_with_gdb_brokenness();
388 /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */
389 resolver
->dns_pid
= fork();
391 /* If we are the child process... */
392 if (resolver
->dns_pid
== 0) {
393 /* We should not access the parent's side of the pipes, so close them */
397 purple_dnsquery_resolver_run(child_out
[1], child_in
[0], show_debug
);
398 /* The thread calls _exit() rather than returning, so we never get here */
401 /* We should not access the child's side of the pipes, so close them */
404 if (resolver
->dns_pid
== -1) {
405 purple_debug_error("dns",
406 "Could not create child process for DNS: %s\n",
408 purple_dnsquery_resolver_destroy(resolver
);
412 resolver
->fd_out
= child_out
[0];
413 resolver
->fd_in
= child_in
[1];
414 number_of_dns_children
++;
415 purple_debug_info("dns",
416 "Created new DNS child %d, there are now %d children.\n",
417 resolver
->dns_pid
, number_of_dns_children
);
423 * @return TRUE if the request was sent succesfully. FALSE
424 * if the request could not be sent. This isn't
425 * necessarily an error. If the child has expired,
426 * for example, we won't be able to send the message.
429 send_dns_request_to_child(PurpleDnsQueryData
*query_data
,
430 PurpleDnsQueryResolverProcess
*resolver
)
433 dns_params_t dns_params
;
436 /* This waitpid might return the child's PID if it has recently
437 * exited, or it might return an error if it exited "long
438 * enough" ago that it has already been reaped; in either
439 * instance, we can't use it. */
440 pid
= waitpid(resolver
->dns_pid
, NULL
, WNOHANG
);
442 purple_debug_warning("dns", "DNS child %d no longer exists\n",
444 purple_dnsquery_resolver_destroy(resolver
);
446 } else if (pid
< 0) {
447 purple_debug_warning("dns", "Wait for DNS child %d failed: %s\n",
448 resolver
->dns_pid
, g_strerror(errno
));
449 purple_dnsquery_resolver_destroy(resolver
);
453 /* Copy the hostname and port into a single data structure */
454 strncpy(dns_params
.hostname
, query_data
->hostname
, sizeof(dns_params
.hostname
) - 1);
455 dns_params
.hostname
[sizeof(dns_params
.hostname
) - 1] = '\0';
456 dns_params
.port
= query_data
->port
;
458 /* Send the data structure to the child */
459 rc
= write(resolver
->fd_in
, &dns_params
, sizeof(dns_params
));
461 purple_debug_error("dns", "Unable to write to DNS child %d: %s\n",
462 resolver
->dns_pid
, g_strerror(errno
));
463 purple_dnsquery_resolver_destroy(resolver
);
466 if (rc
< sizeof(dns_params
)) {
467 purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT
468 " bytes to child but only wrote %" G_GSSIZE_FORMAT
"\n",
469 sizeof(dns_params
), rc
);
470 purple_dnsquery_resolver_destroy(resolver
);
474 purple_debug_info("dns",
475 "Successfully sent DNS request to child %d\n",
478 query_data
->resolver
= resolver
;
483 static void host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
);
486 handle_next_queued_request(void)
488 PurpleDnsQueryData
*query_data
;
489 PurpleDnsQueryResolverProcess
*resolver
;
491 if (queued_requests
== NULL
)
492 /* No more DNS queries, yay! */
495 query_data
= queued_requests
->data
;
496 queued_requests
= g_slist_delete_link(queued_requests
, queued_requests
);
499 * If we have any children, attempt to have them perform the DNS
500 * query. If we're able to send the query then resolver will be
501 * set to the PurpleDnsQueryResolverProcess. Otherwise, resolver
502 * will be NULL and we'll need to create a new DNS request child.
504 while (free_dns_children
!= NULL
)
506 resolver
= free_dns_children
->data
;
507 free_dns_children
= g_slist_remove(free_dns_children
, resolver
);
509 if (send_dns_request_to_child(query_data
, resolver
))
510 /* We found an acceptable child, yay */
514 /* We need to create a new DNS request child */
515 if (query_data
->resolver
== NULL
)
517 if (number_of_dns_children
>= MAX_DNS_CHILDREN
)
519 /* Apparently all our children are busy */
520 queued_requests
= g_slist_prepend(queued_requests
, query_data
);
524 resolver
= purple_dnsquery_resolver_new(purple_debug_is_enabled());
525 if (resolver
== NULL
)
527 purple_dnsquery_failed(query_data
, _("Unable to create new resolver process\n"));
530 if (!send_dns_request_to_child(query_data
, resolver
))
532 purple_dnsquery_failed(query_data
, _("Unable to send request to resolver process\n"));
537 query_data
->resolver
->inpa
= purple_input_add(query_data
->resolver
->fd_out
,
538 PURPLE_INPUT_READ
, host_resolved
, query_data
);
542 * End the functions for dealing with the DNS child processes.
546 host_resolved(gpointer data
, gint source
, PurpleInputCondition cond
)
548 PurpleDnsQueryData
*query_data
;
550 GSList
*hosts
= NULL
;
551 struct sockaddr
*addr
= NULL
;
557 purple_debug_info("dns", "Got response for '%s'\n", query_data
->hostname
);
558 purple_input_remove(query_data
->resolver
->inpa
);
559 query_data
->resolver
->inpa
= 0;
561 rc
= read(query_data
->resolver
->fd_out
, &err
, sizeof(err
));
562 if ((rc
== 4) && (err
!= 0))
564 #ifdef HAVE_GETADDRINFO
565 g_snprintf(message
, sizeof(message
), _("Error resolving %s:\n%s"),
566 query_data
->hostname
, purple_gai_strerror(err
));
568 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
569 query_data
->hostname
, err
);
571 purple_dnsquery_failed(query_data
, message
);
576 rc
= read(query_data
->resolver
->fd_out
, &addrlen
, sizeof(addrlen
));
577 if (rc
> 0 && addrlen
> 0) {
578 addr
= g_malloc(addrlen
);
579 rc
= read(query_data
->resolver
->fd_out
, addr
, addrlen
);
580 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(addrlen
));
581 hosts
= g_slist_append(hosts
, addr
);
586 /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */
587 purple_dnsquery_resolved(query_data
, hosts
);
589 } else if (rc
== -1) {
590 g_snprintf(message
, sizeof(message
), _("Error reading from resolver process:\n%s"), g_strerror(errno
));
591 purple_dnsquery_failed(query_data
, message
);
593 } else if (rc
== 0) {
594 g_snprintf(message
, sizeof(message
), _("Resolver process exited without answering our request"));
595 purple_dnsquery_failed(query_data
, message
);
598 handle_next_queued_request();
602 resolve_host(gpointer data
)
604 PurpleDnsQueryData
*query_data
;
607 query_data
->timeout
= 0;
609 if (purple_dnsquery_ui_resolve(query_data
))
611 /* The UI is handling the resolve; we're done */
615 handle_next_queued_request();
621 purple_dnsquery_a(const char *hostname
, int port
,
622 PurpleDnsQueryConnectFunction callback
, gpointer data
)
624 PurpleDnsQueryData
*query_data
;
626 g_return_val_if_fail(hostname
!= NULL
, NULL
);
627 g_return_val_if_fail(port
!= 0, NULL
);
628 g_return_val_if_fail(callback
!= NULL
, NULL
);
630 query_data
= g_new(PurpleDnsQueryData
, 1);
631 query_data
->hostname
= g_strdup(hostname
);
632 g_strstrip(query_data
->hostname
);
633 query_data
->port
= port
;
634 query_data
->callback
= callback
;
635 query_data
->data
= data
;
636 query_data
->resolver
= NULL
;
638 if (*query_data
->hostname
== '\0')
640 purple_dnsquery_destroy(query_data
);
641 g_return_val_if_reached(NULL
);
644 queued_requests
= g_slist_append(queued_requests
, query_data
);
646 purple_debug_info("dns", "DNS query for '%s' queued\n", query_data
->hostname
);
648 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
653 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
660 dns_main_thread_cb(gpointer data
)
662 PurpleDnsQueryData
*query_data
= data
;
664 /* We're done, so purple_dnsquery_destroy() shouldn't think it is canceling an in-progress lookup */
665 query_data
->resolver
= NULL
;
667 if (query_data
->error_message
!= NULL
)
668 purple_dnsquery_failed(query_data
, query_data
->error_message
);
673 /* We don't want purple_dns_query_resolved() to free(hosts) */
674 hosts
= query_data
->hosts
;
675 query_data
->hosts
= NULL
;
676 purple_dnsquery_resolved(query_data
, hosts
);
683 dns_thread(gpointer data
)
685 PurpleDnsQueryData
*query_data
;
686 #ifdef HAVE_GETADDRINFO
688 struct addrinfo hints
, *res
, *tmp
;
691 struct sockaddr_in sin
;
697 #ifdef HAVE_GETADDRINFO
698 g_snprintf(servname
, sizeof(servname
), "%d", query_data
->port
);
699 memset(&hints
,0,sizeof(hints
));
702 * This is only used to convert a service
703 * name to a port number. As we know we are
704 * passing a number already, we know this
705 * value will not be really used by the C
708 hints
.ai_socktype
= SOCK_STREAM
;
709 if ((rc
= getaddrinfo(query_data
->hostname
, servname
, &hints
, &res
)) == 0) {
712 query_data
->hosts
= g_slist_append(query_data
->hosts
,
713 GSIZE_TO_POINTER(res
->ai_addrlen
));
714 query_data
->hosts
= g_slist_append(query_data
->hosts
,
715 g_memdup(res
->ai_addr
, res
->ai_addrlen
));
720 query_data
->error_message
= g_strdup_printf(_("Error resolving %s:\n%s"), query_data
->hostname
, purple_gai_strerror(rc
));
723 if ((hp
= gethostbyname(query_data
->hostname
))) {
724 memset(&sin
, 0, sizeof(struct sockaddr_in
));
725 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
726 sin
.sin_family
= hp
->h_addrtype
;
727 sin
.sin_port
= htons(query_data
->port
);
729 query_data
->hosts
= g_slist_append(query_data
->hosts
,
730 GSIZE_TO_POINTER(sizeof(sin
)));
731 query_data
->hosts
= g_slist_append(query_data
->hosts
,
732 g_memdup(&sin
, sizeof(sin
)));
734 query_data
->error_message
= g_strdup_printf(_("Error resolving %s: %d"), query_data
->hostname
, h_errno
);
738 /* back to main thread */
739 purple_timeout_add(0, dns_main_thread_cb
, query_data
);
745 resolve_host(gpointer data
)
747 PurpleDnsQueryData
*query_data
;
748 struct sockaddr_in sin
;
752 query_data
->timeout
= 0;
754 if (purple_dnsquery_ui_resolve(query_data
))
756 /* The UI is handling the resolve; we're done */
760 if (inet_aton(query_data
->hostname
, &sin
.sin_addr
))
763 * The given "hostname" is actually an IP address, so we
764 * don't need to do anything.
766 GSList
*hosts
= NULL
;
767 sin
.sin_family
= AF_INET
;
768 sin
.sin_port
= htons(query_data
->port
);
769 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
770 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
771 purple_dnsquery_resolved(query_data
, hosts
);
776 * Spin off a separate thread to perform the DNS lookup so
777 * that we don't block the UI.
779 query_data
->resolver
= g_thread_create(dns_thread
,
780 query_data
, FALSE
, &err
);
781 if (query_data
->resolver
== NULL
)
784 g_snprintf(message
, sizeof(message
), _("Thread creation failure: %s"),
785 (err
&& err
->message
) ? err
->message
: _("Unknown reason"));
787 purple_dnsquery_failed(query_data
, message
);
795 purple_dnsquery_a(const char *hostname
, int port
,
796 PurpleDnsQueryConnectFunction callback
, gpointer data
)
798 PurpleDnsQueryData
*query_data
;
800 g_return_val_if_fail(hostname
!= NULL
, NULL
);
801 g_return_val_if_fail(port
!= 0, NULL
);
802 g_return_val_if_fail(callback
!= NULL
, NULL
);
804 purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname
);
806 query_data
= g_new0(PurpleDnsQueryData
, 1);
807 query_data
->hostname
= g_strdup(hostname
);
808 g_strstrip(query_data
->hostname
);
809 query_data
->port
= port
;
810 query_data
->callback
= callback
;
811 query_data
->data
= data
;
813 if (strlen(query_data
->hostname
) == 0)
815 purple_dnsquery_destroy(query_data
);
816 g_return_val_if_reached(NULL
);
819 /* Don't call the callback before returning */
820 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
825 #else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
828 * We weren't able to do anything fancier above, so use the
829 * fail-safe name resolution code, which is blocking.
833 resolve_host(gpointer data
)
835 PurpleDnsQueryData
*query_data
;
836 struct sockaddr_in sin
;
837 GSList
*hosts
= NULL
;
840 query_data
->timeout
= 0;
842 if (purple_dnsquery_ui_resolve(query_data
))
844 /* The UI is handling the resolve; we're done */
848 if (!inet_aton(query_data
->hostname
, &sin
.sin_addr
)) {
850 if(!(hp
= gethostbyname(query_data
->hostname
))) {
852 g_snprintf(message
, sizeof(message
), _("Error resolving %s: %d"),
853 query_data
->hostname
, h_errno
);
854 purple_dnsquery_failed(query_data
, message
);
857 memset(&sin
, 0, sizeof(struct sockaddr_in
));
858 memcpy(&sin
.sin_addr
.s_addr
, hp
->h_addr
, hp
->h_length
);
859 sin
.sin_family
= hp
->h_addrtype
;
861 sin
.sin_family
= AF_INET
;
862 sin
.sin_port
= htons(query_data
->port
);
864 hosts
= g_slist_append(hosts
, GINT_TO_POINTER(sizeof(sin
)));
865 hosts
= g_slist_append(hosts
, g_memdup(&sin
, sizeof(sin
)));
867 purple_dnsquery_resolved(query_data
, hosts
);
873 purple_dnsquery_a(const char *hostname
, int port
,
874 PurpleDnsQueryConnectFunction callback
, gpointer data
)
876 PurpleDnsQueryData
*query_data
;
878 g_return_val_if_fail(hostname
!= NULL
, NULL
);
879 g_return_val_if_fail(port
!= 0, NULL
);
880 g_return_val_if_fail(callback
!= NULL
, NULL
);
882 query_data
= g_new(PurpleDnsQueryData
, 1);
883 query_data
->hostname
= g_strdup(hostname
);
884 g_strstrip(query_data
->hostname
);
885 query_data
->port
= port
;
886 query_data
->callback
= callback
;
887 query_data
->data
= data
;
889 if (strlen(query_data
->hostname
) == 0)
891 purple_dnsquery_destroy(query_data
);
892 g_return_val_if_reached(NULL
);
895 /* Don't call the callback before returning */
896 query_data
->timeout
= purple_timeout_add(0, resolve_host
, query_data
);
901 #endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
904 purple_dnsquery_destroy(PurpleDnsQueryData
*query_data
)
906 PurpleDnsQueryUiOps
*ops
= purple_dnsquery_get_ui_ops();
908 if (ops
&& ops
->destroy
)
909 ops
->destroy(query_data
);
911 #if defined(PURPLE_DNSQUERY_USE_FORK)
912 queued_requests
= g_slist_remove(queued_requests
, query_data
);
914 if (query_data
->resolver
!= NULL
)
916 * This is only non-NULL when we're cancelling an in-progress
917 * query. Ideally we would tell our resolver child to stop
918 * resolving shit and then we would add it back to the
919 * free_dns_children linked list. However, it's hard to tell
920 * children stuff, they just don't listen. So we'll just
921 * kill the process and allow a new child to be started if we
922 * have more stuff to resolve.
924 purple_dnsquery_resolver_destroy(query_data
->resolver
);
925 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
926 if (query_data
->resolver
!= NULL
)
929 * It's not really possible to kill a thread. So instead we
930 * just set the callback to NULL and let the DNS lookup
933 query_data
->callback
= NULL
;
937 while (query_data
->hosts
!= NULL
)
939 /* Discard the length... */
940 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
941 /* Free the address... */
942 g_free(query_data
->hosts
->data
);
943 query_data
->hosts
= g_slist_remove(query_data
->hosts
, query_data
->hosts
->data
);
945 g_free(query_data
->error_message
);
946 #endif /* end _WIN32 */
948 if (query_data
->timeout
> 0)
949 purple_timeout_remove(query_data
->timeout
);
951 g_free(query_data
->hostname
);
956 purple_dnsquery_get_host(PurpleDnsQueryData
*query_data
)
958 g_return_val_if_fail(query_data
!= NULL
, NULL
);
960 return query_data
->hostname
;
964 purple_dnsquery_get_port(PurpleDnsQueryData
*query_data
)
966 g_return_val_if_fail(query_data
!= NULL
, 0);
968 return query_data
->port
;
972 purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps
*ops
)
974 dns_query_ui_ops
= ops
;
977 PurpleDnsQueryUiOps
*
978 purple_dnsquery_get_ui_ops(void)
980 /* It is perfectly acceptable for dns_query_ui_ops to be NULL; this just
981 * means that the default platform-specific implementation will be used.
983 return dns_query_ui_ops
;
987 purple_dnsquery_init(void)
992 purple_dnsquery_uninit(void)
994 #if defined(PURPLE_DNSQUERY_USE_FORK)
995 while (free_dns_children
!= NULL
)
997 purple_dnsquery_resolver_destroy(free_dns_children
->data
);
998 free_dns_children
= g_slist_remove(free_dns_children
, free_dns_children
->data
);
1000 #endif /* end PURPLE_DNSQUERY_USE_FORK */