Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / dnsquery.c
blob14d9ebe890c1204bd70f4279d070e2fc32988574
1 /**
2 * @file dnsquery.c DNS query API
3 * @ingroup core
4 */
6 /* purple
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
28 #include "internal.h"
29 #include "debug.h"
30 #include "dnsquery.h"
31 #include "notify.h"
32 #include "prefs.h"
33 #include "util.h"
35 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
36 #define PURPLE_DNSQUERY_USE_FORK
37 #endif
38 /**************************************************************************
39 * DNS query API
40 **************************************************************************/
42 static PurpleDnsQueryUiOps *dns_query_ui_ops = NULL;
44 typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess;
46 struct _PurpleDnsQueryData {
47 char *hostname;
48 int port;
49 PurpleDnsQueryConnectFunction callback;
50 gpointer data;
51 guint timeout;
53 #if defined(PURPLE_DNSQUERY_USE_FORK)
54 PurpleDnsQueryResolverProcess *resolver;
55 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
56 GThread *resolver;
57 GSList *hosts;
58 gchar *error_message;
59 #endif
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 {
70 guint inpa;
71 int fd_in, fd_out;
72 pid_t dns_pid;
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.
84 typedef struct {
85 char hostname[512];
86 int port;
87 } dns_params_t;
88 #endif /* end PURPLE_DNSQUERY_USE_FORK */
90 static void
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);
96 else
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
101 * to free hosts.
103 while (hosts != NULL)
105 hosts = g_slist_remove(hosts, hosts->data);
106 g_free(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);
123 static void
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);
132 static gboolean
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);
140 return FALSE;
143 #if defined(PURPLE_DNSQUERY_USE_FORK)
146 * Unix!
150 * Begin the DNS resolver child process functions.
152 #ifdef HAVE_SIGNAL_H
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);
167 _exit(1);
169 #endif
171 static void
172 write_to_parent(int fd, const void *buf, size_t count)
174 ssize_t written;
176 written = write(fd, buf, count);
177 if (written != count) {
178 if (written < 0)
179 fprintf(stderr, "dns[%d]: Error writing data to "
180 "parent: %s\n", getpid(), strerror(errno));
181 else
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;
194 int rc;
195 #ifdef HAVE_GETADDRINFO
196 struct addrinfo hints, *res, *tmp;
197 char servname[20];
198 #else
199 struct sockaddr_in sin;
200 const size_t addrlen = sizeof(sin);
201 #endif
203 #ifdef HAVE_SIGNAL_H
204 purple_restore_default_signal_handlers();
205 signal(SIGTRAP, trap_gdb_bug);
206 #endif
209 * We resolve 1 host name for each iteration of this
210 * while loop.
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.
217 while (1) {
218 fd_set fds;
219 struct timeval tv = { .tv_sec = 20, .tv_usec = 0 };
220 FD_ZERO(&fds);
221 FD_SET(child_in, &fds);
222 rc = select(child_in + 1, &fds, NULL, NULL, &tv);
223 if (!rc) {
224 if (show_debug)
225 printf("dns[%d]: nobody needs me... =(\n", getpid());
226 break;
228 rc = read(child_in, &dns_params, sizeof(dns_params_t));
229 if (rc < 0) {
230 fprintf(stderr, "dns[%d]: Error: Could not read dns_params: "
231 "%s\n", getpid(), strerror(errno));
232 break;
234 if (rc == 0) {
235 if (show_debug)
236 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
237 _exit(0);
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(),
242 dns_params.port);
243 _exit(1);
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
256 * library.
258 hints.ai_socktype = SOCK_STREAM;
259 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res);
260 write_to_parent(child_out, &rc, sizeof(rc));
261 if (rc != 0) {
262 close(child_out);
263 if (show_debug)
264 printf("dns[%d] Error: getaddrinfo returned %d\n",
265 getpid(), rc);
266 dns_params.hostname[0] = '\0';
267 continue;
269 tmp = res;
270 while (res) {
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);
274 res = res->ai_next;
276 freeaddrinfo(tmp);
277 #else
278 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) {
279 struct hostent *hp;
280 if (!(hp = gethostbyname(dns_params.hostname))) {
281 write_to_parent(child_out, &h_errno, sizeof(int));
282 close(child_out);
283 if (show_debug)
284 printf("DNS Error: %d\n", h_errno);
285 _exit(0);
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;
290 } else
291 sin.sin_family = AF_INET;
293 sin.sin_port = htons(dns_params.port);
294 rc = 0;
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);
298 #endif
299 write_to_parent(child_out, &zero, sizeof(zero));
300 dns_params.hostname[0] = '\0';
303 close(child_out);
304 close(child_in);
306 _exit(0);
309 * End the DNS resolver child process functions.
313 * Begin the functions for dealing with the DNS child processes.
315 static void
316 cope_with_gdb_brokenness(void)
318 #ifdef __linux__
319 static gboolean already_done = FALSE;
320 char s[256], e[512];
321 int n;
322 pid_t ppid;
324 if(already_done)
325 return;
326 already_done = TRUE;
327 ppid = getppid();
328 snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
329 n = readlink(s, e, sizeof(e));
330 if(n < 0)
331 return;
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");
340 #endif
343 static void
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);
351 resolver->inpa = 0;
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);
365 g_free(resolver);
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));
380 return NULL;
383 resolver = g_new(PurpleDnsQueryResolverProcess, 1);
384 resolver->inpa = 0;
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 */
394 close(child_out[0]);
395 close(child_in[1]);
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 */
402 close(child_out[1]);
403 close(child_in[0]);
404 if (resolver->dns_pid == -1) {
405 purple_debug_error("dns",
406 "Could not create child process for DNS: %s\n",
407 g_strerror(errno));
408 purple_dnsquery_resolver_destroy(resolver);
409 return NULL;
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);
419 return resolver;
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.
428 static gboolean
429 send_dns_request_to_child(PurpleDnsQueryData *query_data,
430 PurpleDnsQueryResolverProcess *resolver)
432 pid_t pid;
433 dns_params_t dns_params;
434 ssize_t rc;
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);
441 if (pid > 0) {
442 purple_debug_warning("dns", "DNS child %d no longer exists\n",
443 resolver->dns_pid);
444 purple_dnsquery_resolver_destroy(resolver);
445 return FALSE;
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);
450 return FALSE;
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));
460 if (rc < 0) {
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);
464 return FALSE;
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);
471 return FALSE;
474 purple_debug_info("dns",
475 "Successfully sent DNS request to child %d\n",
476 resolver->dns_pid);
478 query_data->resolver = resolver;
480 return TRUE;
483 static void host_resolved(gpointer data, gint source, PurpleInputCondition cond);
485 static void
486 handle_next_queued_request(void)
488 PurpleDnsQueryData *query_data;
489 PurpleDnsQueryResolverProcess *resolver;
491 if (queued_requests == NULL)
492 /* No more DNS queries, yay! */
493 return;
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 */
511 break;
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);
521 return;
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"));
528 return;
530 if (!send_dns_request_to_child(query_data, resolver))
532 purple_dnsquery_failed(query_data, _("Unable to send request to resolver process\n"));
533 return;
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.
545 static void
546 host_resolved(gpointer data, gint source, PurpleInputCondition cond)
548 PurpleDnsQueryData *query_data;
549 int rc, err;
550 GSList *hosts = NULL;
551 struct sockaddr *addr = NULL;
552 size_t addrlen;
553 char message[1024];
555 query_data = data;
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));
567 #else
568 g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
569 query_data->hostname, err);
570 #endif
571 purple_dnsquery_failed(query_data, message);
573 } else if (rc > 0) {
574 /* Success! */
575 while (rc > 0) {
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);
582 } else {
583 break;
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();
601 static gboolean
602 resolve_host(gpointer data)
604 PurpleDnsQueryData *query_data;
606 query_data = data;
607 query_data->timeout = 0;
609 if (purple_dnsquery_ui_resolve(query_data))
611 /* The UI is handling the resolve; we're done */
612 return FALSE;
615 handle_next_queued_request();
617 return FALSE;
620 PurpleDnsQueryData *
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);
650 return query_data;
653 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */
656 * Windows!
659 static gboolean
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);
669 else
671 GSList *hosts;
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);
679 return FALSE;
682 static gpointer
683 dns_thread(gpointer data)
685 PurpleDnsQueryData *query_data;
686 #ifdef HAVE_GETADDRINFO
687 int rc;
688 struct addrinfo hints, *res, *tmp;
689 char servname[20];
690 #else
691 struct sockaddr_in sin;
692 struct hostent *hp;
693 #endif
695 query_data = data;
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
706 * library.
708 hints.ai_socktype = SOCK_STREAM;
709 if ((rc = getaddrinfo(query_data->hostname, servname, &hints, &res)) == 0) {
710 tmp = res;
711 while(res) {
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));
716 res = res->ai_next;
718 freeaddrinfo(tmp);
719 } else {
720 query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, purple_gai_strerror(rc));
722 #else
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)));
733 } else {
734 query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno);
736 #endif
738 /* back to main thread */
739 purple_timeout_add(0, dns_main_thread_cb, query_data);
741 return 0;
744 static gboolean
745 resolve_host(gpointer data)
747 PurpleDnsQueryData *query_data;
748 struct sockaddr_in sin;
749 GError *err = NULL;
751 query_data = data;
752 query_data->timeout = 0;
754 if (purple_dnsquery_ui_resolve(query_data))
756 /* The UI is handling the resolve; we're done */
757 return FALSE;
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);
773 else
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)
783 char message[1024];
784 g_snprintf(message, sizeof(message), _("Thread creation failure: %s"),
785 (err && err->message) ? err->message : _("Unknown reason"));
786 g_error_free(err);
787 purple_dnsquery_failed(query_data, message);
791 return FALSE;
794 PurpleDnsQueryData *
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);
822 return 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.
832 static gboolean
833 resolve_host(gpointer data)
835 PurpleDnsQueryData *query_data;
836 struct sockaddr_in sin;
837 GSList *hosts = NULL;
839 query_data = data;
840 query_data->timeout = 0;
842 if (purple_dnsquery_ui_resolve(query_data))
844 /* The UI is handling the resolve; we're done */
845 return FALSE;
848 if (!inet_aton(query_data->hostname, &sin.sin_addr)) {
849 struct hostent *hp;
850 if(!(hp = gethostbyname(query_data->hostname))) {
851 char message[1024];
852 g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
853 query_data->hostname, h_errno);
854 purple_dnsquery_failed(query_data, message);
855 return FALSE;
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;
860 } else
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);
869 return FALSE;
872 PurpleDnsQueryData *
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);
898 return query_data;
901 #endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
903 void
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
931 * finish.
933 query_data->callback = NULL;
934 return;
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);
952 g_free(query_data);
955 char *
956 purple_dnsquery_get_host(PurpleDnsQueryData *query_data)
958 g_return_val_if_fail(query_data != NULL, NULL);
960 return query_data->hostname;
963 unsigned short
964 purple_dnsquery_get_port(PurpleDnsQueryData *query_data)
966 g_return_val_if_fail(query_data != NULL, 0);
968 return query_data->port;
971 void
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;
986 void
987 purple_dnsquery_init(void)
991 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 */