Tue, 22 Dec 2015 20:25:45 -0600
Merged in use-gresolver (pull request #1)
Use gresolver
| libpurple/dnsquery.c | file | annotate | diff | comparison | revisions | |
| libpurple/dnsquery.h | file | annotate | diff | comparison | revisions | |
| libpurple/dnssrv.c | file | annotate | diff | comparison | revisions | |
| libpurple/dnssrv.h | file | annotate | diff | comparison | revisions |
--- a/configure.ac Mon Dec 21 21:51:27 2015 -0600 +++ b/configure.ac Tue Dec 22 20:25:45 2015 -0600 @@ -477,13 +477,13 @@ AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes") dnl ####################################################################### -dnl # Check for GLib 2.20 (required) +dnl # Check for GLib 2.34 (required) dnl ####################################################################### -PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28.0 gio-2.0 gobject-2.0 gthread-2.0], , [ +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.34.0 gio-2.0 gobject-2.0 gthread-2.0], , [ AC_MSG_RESULT(no) AC_MSG_ERROR([ -You must have GLib 2.20.0 or newer development headers installed to build. +You must have GLib 2.34.0 or newer development headers installed to build. If you have these installed already you may need to install pkg-config so I can find them.
--- a/libpurple/Makefile.am Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/Makefile.am Tue Dec 22 20:25:45 2015 -0600 @@ -106,8 +106,6 @@ smiley-parser.c \ smiley-theme.c \ smiley.c \ - dnsquery.c \ - dnssrv.c\ status.c \ stringref.c \ stun.c \ @@ -187,8 +185,6 @@ smiley-parser.h \ smiley-theme.h \ smiley.h \ - dnsquery.h \ - dnssrv.h \ status.h \ stringref.h \ stun.h \
--- a/libpurple/core.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/core.c Tue Dec 22 20:25:45 2015 -0600 @@ -26,7 +26,6 @@ #include "conversation.h" #include "core.h" #include "debug.h" -#include "dnsquery.h" #include "xfer.h" #include "glibcompat.h" #include "http.h" @@ -200,7 +199,6 @@ purple_pounces_init(); _purple_socket_init(); purple_proxy_init(); - purple_dnsquery_init(); purple_sound_init(); purple_ssl_init(); purple_stun_init(); @@ -276,7 +274,6 @@ purple_xfers_uninit(); purple_proxy_uninit(); _purple_socket_uninit(); - purple_dnsquery_uninit(); _purple_image_store_uninit(); purple_network_uninit();
--- a/libpurple/dnsquery.c Mon Dec 21 21:51:27 2015 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1080 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ -#define _PURPLE_DNSQUERY_C_ - -#include "internal.h" -#include "debug.h" -#include "dnsquery.h" -#include "network.h" -#include "notify.h" -#include "prefs.h" -#include "util.h" - -#ifndef _WIN32 -#include <resolv.h> -#endif - -#define MAX_ADDR_RESPONSE_LEN 1048576 - -#if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__) -#define PURPLE_DNSQUERY_USE_FORK -#endif -/************************************************************************** - * DNS query API - **************************************************************************/ - -static PurpleDnsQueryUiOps *dns_query_ui_ops = NULL; - -typedef struct _PurpleDnsQueryResolverProcess PurpleDnsQueryResolverProcess; - -struct _PurpleDnsQueryData { - char *hostname; - int port; - PurpleDnsQueryConnectFunction callback; - gpointer data; - guint timeout; - PurpleAccount *account; - -#if defined(PURPLE_DNSQUERY_USE_FORK) - PurpleDnsQueryResolverProcess *resolver; -#elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */ - GThread *resolver; - GSList *hosts; - gchar *error_message; -#endif -}; - -#if defined(PURPLE_DNSQUERY_USE_FORK) - -#define MAX_DNS_CHILDREN 4 - -/* - * This structure keeps a reference to a child resolver process. - */ -struct _PurpleDnsQueryResolverProcess { - guint inpa; - int fd_in, fd_out; - pid_t dns_pid; -}; - -static GSList *free_dns_children = NULL; -static GQueue *queued_requests = NULL; - -static int number_of_dns_children = 0; - -/* - * This is a convenience struct used to pass data to - * the child resolver process. - */ -typedef struct { - char hostname[512]; - int port; -} dns_params_t; -#endif /* end PURPLE_DNSQUERY_USE_FORK */ - -static void -purple_dnsquery_resolved(PurpleDnsQueryData *query_data, GSList *hosts) -{ - purple_debug_info("dnsquery", "IP resolved for %s\n", query_data->hostname); - if (query_data->callback != NULL) - query_data->callback(hosts, query_data->data, NULL); - else - { - /* - * Callback is a required parameter, but it can get set to - * NULL if we cancel a thread-based DNS lookup. So we need - * to free hosts. - */ - while (hosts != NULL) - { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } - } - -#ifdef PURPLE_DNSQUERY_USE_FORK - /* - * Add the resolver to the list of available resolvers, and set it - * to NULL so that it doesn't get destroyed along with the query_data - */ - if (query_data->resolver) - { - free_dns_children = g_slist_prepend(free_dns_children, query_data->resolver); - query_data->resolver = NULL; - } -#endif /* PURPLE_DNSQUERY_USE_FORK */ - - purple_dnsquery_destroy(query_data); -} - -static void -purple_dnsquery_failed(PurpleDnsQueryData *query_data, const gchar *error_message) -{ - purple_debug_error("dnsquery", "%s\n", error_message); - if (query_data->callback != NULL) - query_data->callback(NULL, query_data->data, error_message); - purple_dnsquery_destroy(query_data); -} - -static gboolean -purple_dnsquery_ui_resolve(PurpleDnsQueryData *query_data) -{ - PurpleDnsQueryUiOps *ops = purple_dnsquery_get_ui_ops(); - - if (ops && ops->resolve_host) - return ops->resolve_host(query_data, purple_dnsquery_resolved, purple_dnsquery_failed); - - return FALSE; -} - -static gboolean -resolve_ip(PurpleDnsQueryData *query_data) -{ -#if defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) - struct addrinfo hints, *res; - char servname[20]; - - g_snprintf(servname, sizeof(servname), "%d", query_data->port); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags |= AI_NUMERICHOST; - - if (0 == getaddrinfo(query_data->hostname, servname, &hints, &res)) - { - GSList *hosts = NULL; - hosts = g_slist_append(hosts, GINT_TO_POINTER(res->ai_addrlen)); - hosts = g_slist_append(hosts, g_memdup(res->ai_addr, res->ai_addrlen)); - purple_dnsquery_resolved(query_data, hosts); - - freeaddrinfo(res); - return TRUE; - } -#else /* defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) */ - struct sockaddr_in sin; - if (inet_aton(query_data->hostname, &sin.sin_addr)) - { - /* - * The given "hostname" is actually an IP address, so we - * don't need to do anything. - */ - GSList *hosts = NULL; - sin.sin_family = AF_INET; - sin.sin_port = htons(query_data->port); - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - purple_dnsquery_resolved(query_data, hosts); - - return TRUE; - } -#endif - - return FALSE; -} - -#ifdef USE_IDN -static gboolean -dns_str_is_ascii(const char *name) -{ - guchar *c; - for (c = (guchar *)name; c && *c; ++c) { - if (*c > 0x7f) - return FALSE; - } - - return TRUE; -} -#endif - -#if defined(PURPLE_DNSQUERY_USE_FORK) - -/* - * Unix! - */ - -/* - * Begin the DNS resolver child process functions. - */ -#ifdef HAVE_SIGNAL_H -G_GNUC_NORETURN static void -trap_gdb_bug(int sig) -{ - const char *message = - "Purple's DNS child got a SIGTRAP signal.\n" - "This can be caused by trying to run purple inside gdb.\n" - "There is a known gdb bug which prevents this. Supposedly purple\n" - "should have detected you were using gdb and used an ugly hack,\n" - "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" - "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; - fputs("\n* * *\n",stderr); - fputs(message,stderr); - fputs("* * *\n\n",stderr); - execlp("xmessage","xmessage","-center", message, NULL); - _exit(1); -} -#endif - -static void -write_to_parent(int fd, const void *buf, size_t count) -{ - ssize_t written; - - written = write(fd, buf, count); - if (written < 0 || (gsize)written != count) { - if (written < 0) - fprintf(stderr, "dns[%d]: Error writing data to " - "parent: %s\n", getpid(), strerror(errno)); - else - fprintf(stderr, "dns[%d]: Error: Tried to write %" - G_GSIZE_FORMAT " bytes to parent but instead " - "wrote %" G_GSIZE_FORMAT " bytes\n", - getpid(), count, written); - } -} - -G_GNUC_NORETURN static void -purple_dnsquery_resolver_run(int child_out, int child_in, gboolean show_debug) -{ - dns_params_t dns_params; - const size_t zero = 0; - int rc; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - const size_t addrlen = sizeof(sin); -#endif - char *hostname; - -#ifdef HAVE_SIGNAL_H - purple_restore_default_signal_handlers(); - signal(SIGTRAP, trap_gdb_bug); -#endif - - /* - * We resolve 1 host name for each iteration of this - * while loop. - * - * The top half of this reads in the hostname and port - * number from the socket with our parent. The bottom - * half of this resolves the IP (blocking) and sends - * the result back to our parent, when finished. - */ - while (1) { - fd_set fds; - struct timeval tv = { .tv_sec = 20, .tv_usec = 0 }; - FD_ZERO(&fds); - FD_SET(child_in, &fds); - rc = select(child_in + 1, &fds, NULL, NULL, &tv); - if (!rc) { - if (show_debug) - printf("dns[%d]: nobody needs me... =(\n", getpid()); - break; - } - rc = read(child_in, &dns_params, sizeof(dns_params_t)); - if (rc < 0) { - if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { - /* Try again */ - continue; - } - fprintf(stderr, "dns[%d]: Error: Could not read dns_params: " - "%s\n", getpid(), strerror(errno)); - break; - } - if (rc == 0) { - if (show_debug) - printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); - _exit(0); - } - if (dns_params.hostname[0] == '\0') { - fprintf(stderr, "dns[%d]: Error: Parent requested resolution " - "of an empty hostname (port = %d)!!!\n", getpid(), - dns_params.port); - _exit(1); - } - -#ifdef USE_IDN - if (!dns_str_is_ascii(dns_params.hostname)) { - rc = purple_network_convert_idn_to_ascii(dns_params.hostname, &hostname); - if (rc != 0) { - write_to_parent(child_out, &rc, sizeof(rc)); - if (show_debug) - fprintf(stderr, "dns[%d] Error: IDN conversion returned " - "%d\n", getpid(), rc); - dns_params.hostname[0] = '\0'; - break; - } - } else /* intentional to execute the g_strdup */ -#endif - hostname = g_strdup(dns_params.hostname); - - /* We have the hostname and port, now resolve the IP */ - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", dns_params.port); - memset(&hints, 0, sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif /* AI_ADDRCONFIG */ - rc = getaddrinfo(hostname, servname, &hints, &res); - write_to_parent(child_out, &rc, sizeof(rc)); - if (rc != 0) { - if (show_debug) - printf("dns[%d] Error: getaddrinfo returned %d\n", - getpid(), rc); - dns_params.hostname[0] = '\0'; - g_free(hostname); - hostname = NULL; - break; - } - tmp = res; - while (res) { - size_t ai_addrlen = res->ai_addrlen; - write_to_parent(child_out, &ai_addrlen, sizeof(ai_addrlen)); - write_to_parent(child_out, res->ai_addr, res->ai_addrlen); - res = res->ai_next; - } - freeaddrinfo(tmp); -#else - struct hostent *hp; - if (!(hp = gethostbyname(hostname))) { - write_to_parent(child_out, &h_errno, sizeof(int)); - close(child_out); - if (show_debug) - printf("DNS Error: %d\n", h_errno); - _exit(0); - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - - sin.sin_port = htons(dns_params.port); - rc = 0; - write_to_parent(child_out, &rc, sizeof(rc)); - write_to_parent(child_out, &addrlen, sizeof(addrlen)); - write_to_parent(child_out, &sin, addrlen); -#endif - write_to_parent(child_out, &zero, sizeof(zero)); - dns_params.hostname[0] = '\0'; - - g_free(hostname); - hostname = NULL; - } - - close(child_out); - close(child_in); - - _exit(0); -} -/* - * End the DNS resolver child process functions. - */ - -/* - * Begin the functions for dealing with the DNS child processes. - */ -static void -cope_with_gdb_brokenness(void) -{ -#ifdef __linux__ - static gboolean already_done = FALSE; - char s[256], e[512]; - int n; - pid_t ppid; - - if(already_done) - return; - already_done = TRUE; - ppid = getppid(); - g_snprintf(s, sizeof(s), "/proc/%d/exe", ppid); - n = readlink(s, e, sizeof(e)); - if(n < 0) - return; - - e[MIN((gsize)n,sizeof(e)-1)] = '\0'; - - if(strstr(e,"gdb")) { - purple_debug_info("dns", - "Debugger detected, performing useless query...\n"); - gethostbyname("x.x.x.x.x"); - } -#endif -} - -static void -purple_dnsquery_resolver_destroy(PurpleDnsQueryResolverProcess *resolver) -{ - g_return_if_fail(resolver != NULL); - - /* Keep this before the kill() call below. */ - if (resolver->inpa != 0) { - purple_input_remove(resolver->inpa); - resolver->inpa = 0; - } - - /* - * We might as well attempt to kill our child process. It really - * doesn't matter if this fails, because children will expire on - * their own after a few seconds. - */ - if (resolver->dns_pid > 0) - kill(resolver->dns_pid, SIGKILL); - - close(resolver->fd_in); - close(resolver->fd_out); - - g_free(resolver); - - number_of_dns_children--; -} - -static PurpleDnsQueryResolverProcess * -purple_dnsquery_resolver_new(gboolean show_debug) -{ - PurpleDnsQueryResolverProcess *resolver; - int child_out[2], child_in[2]; - - /* Create pipes for communicating with the child process */ - if (pipe(child_out) || pipe(child_in)) { - purple_debug_error("dns", - "Could not create pipes: %s\n", g_strerror(errno)); - return NULL; - } - - resolver = g_new(PurpleDnsQueryResolverProcess, 1); - resolver->inpa = 0; - - cope_with_gdb_brokenness(); - - /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */ - resolver->dns_pid = fork(); - - /* If we are the child process... */ - if (resolver->dns_pid == 0) { - /* We should not access the parent's side of the pipes, so close them */ - close(child_out[0]); - close(child_in[1]); - - purple_dnsquery_resolver_run(child_out[1], child_in[0], show_debug); - /* The thread calls _exit() rather than returning, so we never get here */ - } - - /* We should not access the child's side of the pipes, so close them */ - close(child_out[1]); - close(child_in[0]); - if (resolver->dns_pid == -1) { - purple_debug_error("dns", - "Could not create child process for DNS: %s\n", - g_strerror(errno)); - purple_dnsquery_resolver_destroy(resolver); - return NULL; - } - - resolver->fd_out = child_out[0]; - resolver->fd_in = child_in[1]; - number_of_dns_children++; - purple_debug_info("dns", - "Created new DNS child %d, there are now %d children.\n", - resolver->dns_pid, number_of_dns_children); - - return resolver; -} - -/* - * send_dns_request_to_child: - * - * Returns: TRUE if the request was sent succesfully. FALSE - * if the request could not be sent. This isn't - * necessarily an error. If the child has expired, - * for example, we won't be able to send the message. - */ -static gboolean -send_dns_request_to_child(PurpleDnsQueryData *query_data, - PurpleDnsQueryResolverProcess *resolver) -{ - pid_t pid; - dns_params_t dns_params; - ssize_t rc; - - /* This waitpid might return the child's PID if it has recently - * exited, or it might return an error if it exited "long - * enough" ago that it has already been reaped; in either - * instance, we can't use it. */ - pid = waitpid(resolver->dns_pid, NULL, WNOHANG); - if (pid > 0) { - purple_debug_info("dns", "DNS child %d no longer exists\n", - resolver->dns_pid); - purple_dnsquery_resolver_destroy(resolver); - return FALSE; - } else if (pid < 0) { - purple_debug_info("dns", "Wait for DNS child %d failed: %s\n", - resolver->dns_pid, g_strerror(errno)); - purple_dnsquery_resolver_destroy(resolver); - return FALSE; - } - - /* Copy the hostname and port into a single data structure */ - strncpy(dns_params.hostname, query_data->hostname, sizeof(dns_params.hostname) - 1); - dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; - dns_params.port = query_data->port; - - /* Send the data structure to the child */ - rc = write(resolver->fd_in, &dns_params, sizeof(dns_params)); - if (rc < 0) { - purple_debug_error("dns", "Unable to write to DNS child %d: %s\n", - resolver->dns_pid, g_strerror(errno)); - purple_dnsquery_resolver_destroy(resolver); - return FALSE; - } - if ((gsize)rc < sizeof(dns_params)) { - purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT - " bytes to child but only wrote %" G_GSSIZE_FORMAT "\n", - sizeof(dns_params), rc); - purple_dnsquery_resolver_destroy(resolver); - return FALSE; - } - - purple_debug_info("dns", - "Successfully sent DNS request to child %d\n", - resolver->dns_pid); - - query_data->resolver = resolver; - - return TRUE; -} - -static void host_resolved(gpointer data, gint source, PurpleInputCondition cond); - -static void -handle_next_queued_request(void) -{ - PurpleDnsQueryData *query_data; - PurpleDnsQueryResolverProcess *resolver; - - if (g_queue_is_empty(queued_requests)) - /* No more DNS queries, yay! */ - return; - - query_data = g_queue_pop_head(queued_requests); - - /* - * If we have any children, attempt to have them perform the DNS - * query. If we're able to send the query then resolver will be - * set to the PurpleDnsQueryResolverProcess. Otherwise, resolver - * will be NULL and we'll need to create a new DNS request child. - */ - while (free_dns_children != NULL) - { - resolver = free_dns_children->data; - free_dns_children = g_slist_remove(free_dns_children, resolver); - - if (send_dns_request_to_child(query_data, resolver)) - /* We found an acceptable child, yay */ - break; - } - - /* We need to create a new DNS request child */ - if (query_data->resolver == NULL) - { - if (number_of_dns_children >= MAX_DNS_CHILDREN) - { - /* Apparently all our children are busy */ - g_queue_push_head(queued_requests, query_data); - return; - } - - resolver = purple_dnsquery_resolver_new(purple_debug_is_enabled()); - if (resolver == NULL) - { - purple_dnsquery_failed(query_data, _("Unable to create new resolver process\n")); - return; - } - if (!send_dns_request_to_child(query_data, resolver)) - { - purple_dnsquery_failed(query_data, _("Unable to send request to resolver process\n")); - return; - } - } - - query_data->resolver->inpa = purple_input_add(query_data->resolver->fd_out, - PURPLE_INPUT_READ, host_resolved, query_data); -} - -/* - * End the functions for dealing with the DNS child processes. - */ - -static void -host_resolved(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleDnsQueryData *query_data; - int rc, err; - GSList *hosts = NULL; - struct sockaddr *addr = NULL; - size_t addrlen; - char message[1024]; - - query_data = data; - - purple_debug_info("dns", "Got response for '%s'\n", query_data->hostname); - purple_input_remove(query_data->resolver->inpa); - query_data->resolver->inpa = 0; - - rc = read(query_data->resolver->fd_out, &err, sizeof(err)); - if ((rc == 4) && (err != 0)) - { -#ifdef HAVE_GETADDRINFO - g_snprintf(message, sizeof(message), _("Error resolving %s:\n%s"), - query_data->hostname, purple_gai_strerror(err)); -#else - g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), - query_data->hostname, err); -#endif - /* Re-read resolv.conf and friends in case DNS servers have changed */ - res_init(); - - purple_dnsquery_failed(query_data, message); - } else if (rc > 0) { - /* Success! */ - while (rc > 0) { - rc = read(query_data->resolver->fd_out, &addrlen, sizeof(addrlen)); - if (rc > 0 && addrlen > 0 && addrlen < MAX_ADDR_RESPONSE_LEN) { - addr = g_malloc(addrlen); - rc = read(query_data->resolver->fd_out, addr, addrlen); - hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); - hosts = g_slist_append(hosts, addr); - } else { - break; - } - } - /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */ - purple_dnsquery_resolved(query_data, hosts); - - } else if (rc == -1) { - g_snprintf(message, sizeof(message), _("Error reading from resolver process:\n%s"), g_strerror(errno)); - purple_dnsquery_failed(query_data, message); - - } else if (rc == 0) { - g_snprintf(message, sizeof(message), _("Resolver process exited without answering our request")); - purple_dnsquery_failed(query_data, message); - } - - handle_next_queued_request(); -} - -static void -resolve_host(PurpleDnsQueryData *query_data) -{ - g_queue_push_tail(queued_requests, query_data); - - handle_next_queued_request(); -} - -#elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */ - -/* - * Windows! - */ - -static gboolean -dns_main_thread_cb(gpointer data) -{ - PurpleDnsQueryData *query_data = data; - - /* We're done, so purple_dnsquery_destroy() shouldn't think it is canceling an in-progress lookup */ - query_data->resolver = NULL; - - if (query_data->error_message != NULL) - purple_dnsquery_failed(query_data, query_data->error_message); - else - { - GSList *hosts; - - /* We don't want purple_dns_query_resolved() to free(hosts) */ - hosts = query_data->hosts; - query_data->hosts = NULL; - purple_dnsquery_resolved(query_data, hosts); - } - - return FALSE; -} - -static gpointer -dns_thread(gpointer data) -{ - PurpleDnsQueryData *query_data; -#if defined(HAVE_GETADDRINFO) || defined(USE_IDN) - int rc; -#endif -#ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - struct hostent *hp; -#endif - char *hostname; - - query_data = data; - -#ifdef USE_IDN - if (!dns_str_is_ascii(query_data->hostname)) { - rc = purple_network_convert_idn_to_ascii(query_data->hostname, &hostname); - if (rc != 0) { - query_data->error_message = g_strdup_printf(_("Error converting %s " - "to punycode: %d"), query_data->hostname, rc); - /* back to main thread */ - purple_timeout_add(0, dns_main_thread_cb, query_data); - return 0; - } - } else /* intentional fallthru */ -#endif - hostname = g_strdup(query_data->hostname); - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", query_data->port); - memset(&hints,0,sizeof(hints)); - - /* - * This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif /* AI_ADDRCONFIG */ - if ((rc = getaddrinfo(hostname, servname, &hints, &res)) == 0) { - tmp = res; - while(res) { - query_data->hosts = g_slist_append(query_data->hosts, - GSIZE_TO_POINTER(res->ai_addrlen)); - query_data->hosts = g_slist_append(query_data->hosts, - g_memdup(res->ai_addr, res->ai_addrlen)); - res = res->ai_next; - } - freeaddrinfo(tmp); - } else { - query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, purple_gai_strerror(rc)); - } -#else - if ((hp = gethostbyname(hostname))) { - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - sin.sin_port = htons(query_data->port); - - query_data->hosts = g_slist_append(query_data->hosts, - GSIZE_TO_POINTER(sizeof(sin))); - query_data->hosts = g_slist_append(query_data->hosts, - g_memdup(&sin, sizeof(sin))); - } else { - query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno); - } -#endif - g_free(hostname); - - /* back to main thread */ - purple_timeout_add(0, dns_main_thread_cb, query_data); - - return 0; -} - -static void -resolve_host(PurpleDnsQueryData *query_data) -{ - GError *err = NULL; - - /* - * Spin off a separate thread to perform the DNS lookup so - * that we don't block the UI. - */ - query_data->resolver = g_thread_try_new("dnsquery resolver", dns_thread, - query_data, &err); - if (query_data->resolver == NULL) - { - char message[1024]; - g_snprintf(message, sizeof(message), _("Thread creation failure: %s"), - (err && err->message) ? err->message : _("Unknown reason")); - g_error_free(err); - purple_dnsquery_failed(query_data, message); - } - else - g_thread_unref(query_data->resolver); -} - -#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ - -/* - * We weren't able to do anything fancier above, so use the - * fail-safe name resolution code, which is blocking. - */ - -static void -resolve_host(PurpleDnsQueryData *query_data) -{ - struct sockaddr_in sin; - GSList *hosts = NULL; - struct hostent *hp; - gchar *hostname; -#ifdef USE_IDN - if (!dns_str_is_ascii(query_data->hostname)) { - int ret = purple_network_convert_idn_to_ascii(query_data->hostname, - &hostname); - if (ret != 0) { - char message[1024]; - g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), - query_data->hostname, ret); - purple_dnsquery_failed(query_data, message); - return; - } - } else /* fallthrough is intentional to the g_strdup */ -#endif - hostname = g_strdup(query_data->hostname); - - if(!(hp = gethostbyname(hostname))) { - char message[1024]; - g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), - query_data->hostname, h_errno); - purple_dnsquery_failed(query_data, message); - g_free(hostname); - return; - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - g_free(hostname); - sin.sin_port = htons(query_data->port); - - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - - purple_dnsquery_resolved(query_data, hosts); -} - -#endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */ - -static gboolean -initiate_resolving(gpointer data) -{ - PurpleDnsQueryData *query_data; - PurpleProxyType proxy_type; - - query_data = data; - query_data->timeout = 0; - - if (resolve_ip(query_data)) - /* resolve_ip calls purple_dnsquery_resolved */ - return FALSE; - - proxy_type = purple_proxy_info_get_proxy_type( - purple_proxy_get_setup(query_data->account)); - if (proxy_type == PURPLE_PROXY_TOR) { - purple_dnsquery_failed(query_data, - _("Aborting DNS lookup in Tor Proxy mode.")); - return FALSE; - } - - if (purple_dnsquery_ui_resolve(query_data)) - /* The UI is handling the resolve; we're done */ - return FALSE; - - resolve_host(query_data); - - return FALSE; -} - -PurpleDnsQueryData * -purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port, - PurpleDnsQueryConnectFunction callback, gpointer data) -{ - PurpleDnsQueryData *query_data; - - g_return_val_if_fail(hostname != NULL, NULL); - g_return_val_if_fail(port != 0, NULL); - g_return_val_if_fail(callback != NULL, NULL); - - purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname); - - query_data = g_new0(PurpleDnsQueryData, 1); - query_data->hostname = g_strdup(hostname); - g_strstrip(query_data->hostname); - query_data->port = port; - query_data->callback = callback; - query_data->data = data; - query_data->account = account; - - if (*query_data->hostname == '\0') - { - purple_dnsquery_destroy(query_data); - g_return_val_if_reached(NULL); - } - - query_data->timeout = purple_timeout_add(0, initiate_resolving, query_data); - - return query_data; -} - -void -purple_dnsquery_destroy(PurpleDnsQueryData *query_data) -{ - PurpleDnsQueryUiOps *ops = purple_dnsquery_get_ui_ops(); - - if (ops && ops->destroy) - ops->destroy(query_data); - -#if defined(PURPLE_DNSQUERY_USE_FORK) - g_queue_remove(queued_requests, query_data); - - if (query_data->resolver != NULL) - /* - * This is only non-NULL when we're cancelling an in-progress - * query. Ideally we would tell our resolver child to stop - * resolving shit and then we would add it back to the - * free_dns_children linked list. However, it's hard to tell - * children stuff, they just don't listen. So we'll just - * kill the process and allow a new child to be started if we - * have more stuff to resolve. - */ - purple_dnsquery_resolver_destroy(query_data->resolver); -#elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK */ - if (query_data->resolver != NULL) - { - /* - * It's not really possible to kill a thread. So instead we - * just set the callback to NULL and let the DNS lookup - * finish. - */ - query_data->callback = NULL; - return; - } - - while (query_data->hosts != NULL) - { - /* Discard the length... */ - query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); - /* Free the address... */ - g_free(query_data->hosts->data); - query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); - } - g_free(query_data->error_message); -#endif /* end _WIN32 */ - - if (query_data->timeout > 0) - purple_timeout_remove(query_data->timeout); - - g_free(query_data->hostname); - g_free(query_data); -} - -char * -purple_dnsquery_get_host(PurpleDnsQueryData *query_data) -{ - g_return_val_if_fail(query_data != NULL, NULL); - - return query_data->hostname; -} - -unsigned short -purple_dnsquery_get_port(PurpleDnsQueryData *query_data) -{ - g_return_val_if_fail(query_data != NULL, 0); - - return query_data->port; -} - -static PurpleDnsQueryUiOps * -purple_dnsquery_ui_ops_copy(PurpleDnsQueryUiOps *ops) -{ - PurpleDnsQueryUiOps *ops_new; - - g_return_val_if_fail(ops != NULL, NULL); - - ops_new = g_new(PurpleDnsQueryUiOps, 1); - *ops_new = *ops; - - return ops_new; -} - -GType -purple_dnsquery_ui_ops_get_type(void) -{ - static GType type = 0; - - if (type == 0) { - type = g_boxed_type_register_static("PurpleDnsQueryUiOps", - (GBoxedCopyFunc)purple_dnsquery_ui_ops_copy, - (GBoxedFreeFunc)g_free); - } - - return type; -} - -void -purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps *ops) -{ - dns_query_ui_ops = ops; -} - -PurpleDnsQueryUiOps * -purple_dnsquery_get_ui_ops(void) -{ - /* It is perfectly acceptable for dns_query_ui_ops to be NULL; this just - * means that the default platform-specific implementation will be used. - */ - return dns_query_ui_ops; -} - -void -purple_dnsquery_init(void) -{ -#if defined(PURPLE_DNSQUERY_USE_FORK) - queued_requests = g_queue_new(); -#endif -} - -void -purple_dnsquery_uninit(void) -{ -#if defined(PURPLE_DNSQUERY_USE_FORK) - while (free_dns_children != NULL) - { - purple_dnsquery_resolver_destroy(free_dns_children->data); - free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data); - } - - g_queue_free(queued_requests); - queued_requests = NULL; -#endif /* end PURPLE_DNSQUERY_USE_FORK */ -} -
--- a/libpurple/dnsquery.h Mon Dec 21 21:51:27 2015 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _PURPLE_DNSQUERY_H_ -#define _PURPLE_DNSQUERY_H_ -/** - * SECTION:dnsquery - * @section_id: libpurple-dnsquery - * @short_description: <filename>dnsquery.h</filename> - * @title: DNS Query API - */ - -#include <glib.h> -#include "eventloop.h" -#include "account.h" - -#define PURPLE_TYPE_DNSQUERY_UI_OPS (purple_dnsquery_ui_ops_get_type()) - -/** - * PurpleDnsQueryData: - * - * An opaque structure representing a DNS query. The hostname and port - * associated with the query can be retrieved using - * purple_dnsquery_get_host() and purple_dnsquery_get_port(). - */ -typedef struct _PurpleDnsQueryData PurpleDnsQueryData; -typedef struct _PurpleDnsQueryUiOps PurpleDnsQueryUiOps; - -/** - * PurpleDnsQueryConnectFunction: - * - * The "hosts" parameter is a linked list containing pairs of - * one size_t addrlen and one struct sockaddr *addr. It should - * be free'd by the callback function. - */ -typedef void (*PurpleDnsQueryConnectFunction)(GSList *hosts, gpointer data, const char *error_message); - -/** - * PurpleDnsQueryResolvedCallback: - * - * DNS query resolved callback used by the UI if it handles resolving DNS - */ -typedef void (*PurpleDnsQueryResolvedCallback) (PurpleDnsQueryData *query_data, GSList *hosts); - -/** - * PurpleDnsQueryFailedCallback: - * - * DNS query failed callback used by the UI if it handles resolving DNS - */ -typedef void (*PurpleDnsQueryFailedCallback) (PurpleDnsQueryData *query_data, const gchar *error_message); - -/** - * PurpleDnsQueryUiOps: - * @resolve_host: If implemented, return %TRUE if the UI takes responsibility - * for DNS queries. When returning %FALSE, the standard - * implementation is used. - * @destroy: Called just before @query_data is freed; this should cancel - * any further use of @query_data the UI would make. Unneeded if - * @resolve_host is not implemented. - * - * DNS Request UI operations; UIs should implement this if they want to do DNS - * lookups themselves, rather than relying on the core. - * - * See <link linkend="chapter-ui-ops">List of <literal>UiOps</literal> - * Structures</link> - */ -struct _PurpleDnsQueryUiOps -{ - gboolean (*resolve_host)(PurpleDnsQueryData *query_data, - PurpleDnsQueryResolvedCallback resolved_cb, - PurpleDnsQueryFailedCallback failed_cb); - - void (*destroy)(PurpleDnsQueryData *query_data); - - /*< private >*/ - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -G_BEGIN_DECLS - -/**************************************************************************/ -/* DNS query API */ -/**************************************************************************/ - -/** - * purple_dnsquery_ui_ops_get_type: - * - * Returns: The #GType for the #PurpleDnsQueryUiOps boxed structure. - */ -GType purple_dnsquery_ui_ops_get_type(void); - -/** - * purple_dnsquery_a: - * @account: The account that the query is being done for (or NULL) - * @hostname: The hostname to resolve. - * @port: A port number which is stored in the struct sockaddr. - * @callback: (scope call): The callback function to call after resolving. - * @data: Extra data to pass to the callback function. - * - * Perform an asynchronous DNS query. - * - * Returns: NULL if there was an error, otherwise return a reference to - * a data structure that can be used to cancel the pending - * DNS query, if needed. - * - */ -PurpleDnsQueryData *purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data); - -/** - * purple_dnsquery_destroy: - * @query_data: The DNS query to cancel. This data structure - * is freed by this function. - * - * Cancel a DNS query and destroy the associated data structure. - */ -void purple_dnsquery_destroy(PurpleDnsQueryData *query_data); - -/** - * purple_dnsquery_set_ui_ops: - * @ops: The UI operations structure. - * - * Sets the UI operations structure to be used when doing a DNS - * resolve. The UI operations need only be set if the UI wants to - * handle the resolve itself; otherwise, leave it as NULL. - */ -void purple_dnsquery_set_ui_ops(PurpleDnsQueryUiOps *ops); - -/** - * purple_dnsquery_get_ui_ops: - * - * Returns the UI operations structure to be used when doing a DNS - * resolve. - * - * Returns: The UI operations structure. - */ -PurpleDnsQueryUiOps *purple_dnsquery_get_ui_ops(void); - -/** - * purple_dnsquery_get_host: - * @query_data: The DNS query - * - * Get the host associated with a PurpleDnsQueryData - * - * Returns: The host. - */ -char *purple_dnsquery_get_host(PurpleDnsQueryData *query_data); - -/** - * purple_dnsquery_get_port: - * @query_data: The DNS query - * - * Get the port associated with a PurpleDnsQueryData - * - * Returns: The port. - */ -unsigned short purple_dnsquery_get_port(PurpleDnsQueryData *query_data); - -/** - * purple_dnsquery_init: - * - * Initializes the DNS query subsystem. - */ -void purple_dnsquery_init(void); - -/** - * purple_dnsquery_uninit: - * - * Uninitializes the DNS query subsystem. - */ -void purple_dnsquery_uninit(void); - -G_END_DECLS - -#endif /* _PURPLE_DNSQUERY_H_ */
--- a/libpurple/dnssrv.c Mon Dec 21 21:51:27 2015 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1163 +0,0 @@ -/* purple - * - * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#define _PURPLE_DNSSRV_C_ - -#include "internal.h" -#include "util.h" - -#ifndef _WIN32 -#include <arpa/nameser.h> -#include <resolv.h> -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include <arpa/nameser_compat.h> -#endif -#else /* WIN32 */ -#include <windns.h> -/* Missing from the mingw headers */ -#ifndef DNS_TYPE_SRV -# define DNS_TYPE_SRV PurpleDnsTypeSrv -#endif -#ifndef DNS_TYPE_TXT -# define DNS_TYPE_TXT PurpleDnsTypeTxt -#endif -#endif - -#ifndef T_SRV -#define T_SRV PurpleDnsTypeSrv -#endif -#ifndef T_TXT -#define T_TXT PurpleDnsTypeTxt -#endif - -#define MAX_ADDR_RESPONSE_LEN 1048576 - -#include "debug.h" -#include "dnssrv.h" -#include "eventloop.h" -#include "network.h" - -static PurpleSrvTxtQueryUiOps *srv_txt_query_ui_ops = NULL; - -#ifndef _WIN32 -typedef union { - HEADER hdr; - u_char buf[1024]; -} queryans; -#endif - -enum PurpleDnsType { - PurpleDnsTypeTxt = 16, - PurpleDnsTypeSrv = 33 -}; - -struct _PurpleSrvTxtQueryData { - union { - PurpleSrvCallback srv; - PurpleTxtCallback txt; - } cb; - - gpointer extradata; - guint handle; - int type; - char *query; -#ifdef _WIN32 - GThread *resolver; - char *error_message; - GList *results; -#else - int fd_in, fd_out; - pid_t pid; -#endif -}; - -typedef struct _PurpleSrvInternalQuery { - int type; - char query[256]; -} PurpleSrvInternalQuery; - -typedef struct _PurpleSrvResponseContainer { - PurpleSrvResponse *response; - int sum; -} PurpleSrvResponseContainer; - -static gboolean purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data); - -/* - * Sort by priority, then by weight. Strictly numerically--no - * randomness. Technically we only need to sort by pref and then - * make sure any records with weight 0 are at the beginning of - * their group, but it's just as easy to sort by weight. - */ -static gint -responsecompare(gconstpointer ar, gconstpointer br) -{ - PurpleSrvResponse *a = (PurpleSrvResponse*)ar; - PurpleSrvResponse *b = (PurpleSrvResponse*)br; - - if(a->pref == b->pref) { - if(a->weight == b->weight) - return 0; - if(a->weight < b->weight) - return -1; - return 1; - } - if(a->pref < b->pref) - return -1; - return 1; -} - -/* - * select_random_response: - * @list: The list of PurpleSrvResponseContainer. This function - * removes a node from this list and returns the new list. - * @container_ptr: The PurpleSrvResponseContainer that was chosen - * will be returned here. - * - * Iterate over a list of PurpleSrvResponseContainer making the sum - * the running total of the sums. Select a random integer in the range - * (1, sum+1), then find the first element greater than or equal to the - * number selected. From RFC 2782. - */ -static GList * -select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr) -{ - GList *cur; - size_t runningtotal; - int r; - - g_return_val_if_fail(list != NULL, NULL); - - runningtotal = 0; - cur = list; - - while (cur) { - PurpleSrvResponseContainer *container = cur->data; - runningtotal += container->response->weight; - container->sum = runningtotal; - cur = cur->next; - } - - /* - * If the running total is greater than 0, pick a number between - * 1 and the runningtotal inclusive. (This is not precisely what - * the RFC algorithm describes, but we wish to deal with integers - * and avoid floats. This is functionally equivalent.) - * If running total is 0, then choose r = 0. - */ - r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0; - cur = list; - while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) { - if (G_UNLIKELY(!cur->next)) - break; - cur = cur->next; - } - - /* Set the return parameter and remove cur from the list */ - *container_ptr = cur->data; - return g_list_delete_link(list, cur); -} - -/* - * Reorder a GList of PurpleSrvResponses that have the same priority - * (aka "pref"). - */ -static void -srv_reorder(GList *list, int num) -{ - int i; - GList *cur, *container_list = NULL; - PurpleSrvResponseContainer *container; - - if (num < 2) - /* Nothing to sort */ - return; - - g_return_if_fail(list != NULL); - - /* First build a list of container structs */ - for (i = 0, cur = list; i < num; i++, cur = cur->next) { - container = g_new(PurpleSrvResponseContainer, 1); - container->response = cur->data; - container_list = g_list_prepend(container_list, container); - } - container_list = g_list_reverse(container_list); - - /* - * Re-order the list that was passed in as a parameter. We leave - * the list nodes in place, but replace their data pointers. - */ - cur = list; - while (container_list) { - g_return_if_fail(cur); - container_list = select_random_response(container_list, &container); - cur->data = container->response; - g_free(container); - cur = cur->next; - } -} - -/* - * purple_srv_sort: - * @list: The original list, resorted - * - * Sorts a GList of PurpleSrvResponses according to the - * algorithm described in RFC 2782. - * - * Returns: GList of PurpleSrvResponse's - */ -static GList * -purple_srv_sort(GList *list) -{ - int pref, count; - GList *cur, *start; - - if (!list || !list->next) { - /* Nothing to sort */ - return list; - } - - list = g_list_sort(list, responsecompare); - - start = cur = list; - count = 1; - while (cur) { - PurpleSrvResponse *next_response; - - g_return_val_if_fail(cur->data, list); - - pref = ((PurpleSrvResponse *)cur->data)->pref; - next_response = cur->next ? cur->next->data : NULL; - if (!next_response || next_response->pref != pref) { - /* - * The 'count' records starting at 'start' all have the same - * priority. Sort them by weight. - */ - srv_reorder(start, count); - start = cur->next; - count = 0; - } - count++; - cur = cur->next; - } - - return list; -} - -static PurpleSrvTxtQueryData * -query_data_new(int type, gchar *query, gpointer extradata) -{ - PurpleSrvTxtQueryData *query_data = g_new0(PurpleSrvTxtQueryData, 1); - query_data->type = type; - query_data->extradata = extradata; - query_data->query = query; -#ifndef _WIN32 - query_data->fd_in = -1; - query_data->fd_out = -1; -#endif - return query_data; -} - -void -purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data) -{ - PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); - - if (ops && ops->destroy) - ops->destroy(query_data); - - if (query_data->handle > 0) - purple_input_remove(query_data->handle); -#ifdef _WIN32 - if (query_data->resolver != NULL) - { - /* - * It's not really possible to kill a thread. So instead we - * just set the callback to NULL and let the DNS lookup - * finish. - */ - query_data->cb.srv = NULL; - return; - } - g_free(query_data->error_message); -#else - if (query_data->fd_out != -1) - close(query_data->fd_out); - if (query_data->fd_in != -1) - close(query_data->fd_in); -#endif - g_free(query_data->query); - g_free(query_data); -} - -#ifdef USE_IDN -static gboolean -dns_str_is_ascii(const char *name) -{ - guchar *c; - for (c = (guchar *)name; c && *c; ++c) { - if (*c > 0x7f) - return FALSE; - } - - return TRUE; -} -#endif - -#ifndef _WIN32 -static void -write_to_parent(int in, int out, gconstpointer data, gsize size) -{ - const guchar *buf = data; - gssize w; - - do { - w = write(out, buf, size); - if (w > 0) { - buf += w; - size -= w; - } else if (w < 0 && errno == EINTR) { - /* Let's try some more; */ - w = 1; - } - } while (size > 0 && w > 0); - - if (size != 0) { - /* An error occurred */ - close(out); - close(in); - _exit(0); - } -} - -/* Read size bytes to data. Dies if an error occurs. */ -static void -read_from_parent(int in, int out, gpointer data, gsize size) -{ - guchar *buf = data; - gssize r; - - do { - r = read(in, data, size); - if (r > 0) { - buf += r; - size -= r; - } else if (r < 0 && errno == EINTR) { - /* Let's try some more; */ - r = 1; - } - } while (size > 0 && r > 0); - - if (size != 0) { - /* An error occurred */ - close(out); - close(in); - _exit(0); - } -} - - -G_GNUC_NORETURN static void -resolve(int in, int out) -{ - GList *ret = NULL; - PurpleSrvResponse *srvres; - PurpleTxtResponse *txtres; - queryans answer; - int size, qdcount, ancount; - guchar *end, *cp; - gchar name[256]; - guint16 type, dlen, pref, weight, port; - PurpleSrvInternalQuery query; - -#ifdef HAVE_SIGNAL_H - purple_restore_default_signal_handlers(); -#endif - - read_from_parent(in, out, &query, sizeof(query)); - - size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer)); - if (size == -1) { - write_to_parent(in, out, &(query.type), sizeof(query.type)); - write_to_parent(in, out, &size, sizeof(size)); - close(out); - close(in); - _exit(0); - } - - qdcount = ntohs(answer.hdr.qdcount); - ancount = ntohs(answer.hdr.ancount); - cp = (guchar*)&answer + sizeof(HEADER); - end = (guchar*)&answer + size; - - /* skip over unwanted stuff */ - while (qdcount-- > 0 && cp < end) { - size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); - if(size < 0) goto end; - cp += size + QFIXEDSZ; - } - - while (ancount-- > 0 && cp < end) { - size = dn_expand((unsigned char*)&answer, end, cp, name, 256); - if(size < 0) - goto end; - cp += size; - GETSHORT(type,cp); - - /* skip ttl and class since we already know it */ - cp += 6; - - GETSHORT(dlen,cp); - if (type == T_SRV) { - GETSHORT(pref,cp); - - GETSHORT(weight,cp); - - GETSHORT(port,cp); - - size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); - if(size < 0 ) - goto end; - - cp += size; - - srvres = g_new0(PurpleSrvResponse, 1); - if (strlen(name) > sizeof(srvres->hostname) - 1) { - purple_debug_error("dnssrv", "hostname is " - "longer than available buffer ('%s', %" - G_GSIZE_FORMAT " bytes)!", - name, strlen(name)); - } - g_strlcpy(srvres->hostname, name, sizeof(srvres->hostname)); - srvres->pref = pref; - srvres->port = port; - srvres->weight = weight; - - ret = g_list_prepend(ret, srvres); - } else if (type == T_TXT) { - txtres = g_new0(PurpleTxtResponse, 1); - txtres->content = g_strndup((gchar*)(++cp), dlen-1); - ret = g_list_append(ret, txtres); - cp += dlen - 1; - } else { - cp += dlen; - } - } - -end: - size = g_list_length(ret); - - if (query.type == T_SRV) - ret = purple_srv_sort(ret); - - write_to_parent(in, out, &(query.type), sizeof(query.type)); - write_to_parent(in, out, &size, sizeof(size)); - while (ret != NULL) - { - if (query.type == T_SRV) - write_to_parent(in, out, ret->data, sizeof(PurpleSrvResponse)); - if (query.type == T_TXT) { - PurpleTxtResponse *response = ret->data; - gsize l = strlen(response->content) + 1 /* null byte */; - write_to_parent(in, out, &l, sizeof(l)); - write_to_parent(in, out, response->content, l); - } - - g_free(ret->data); - ret = g_list_remove(ret, ret->data); - } - - close(out); - close(in); - - _exit(0); -} - -static void -resolved(gpointer data, gint source, PurpleInputCondition cond) -{ - int size; - int type; - PurpleSrvTxtQueryData *query_data = (PurpleSrvTxtQueryData*)data; - int i; - int status; - - if (read(source, &type, sizeof(type)) == sizeof(type)) { - if (read(source, &size, sizeof(size)) == sizeof(size)) { - if (size < -1 || size > MAX_ADDR_RESPONSE_LEN) { - purple_debug_warning("dnssrv", "res_query returned invalid number\n"); - size = 0; - } - if (size == -1 || size == 0) { - if (size == -1) { - purple_debug_warning("dnssrv", "res_query returned an error\n"); - /* Re-read resolv.conf and friends in case DNS servers have changed */ - res_init(); - } else - purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno); - - if (type == T_SRV) { - PurpleSrvCallback cb = query_data->cb.srv; - cb(NULL, 0, query_data->extradata); - } else if (type == T_TXT) { - PurpleTxtCallback cb = query_data->cb.txt; - cb(NULL, query_data->extradata); - } else { - purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno); - } - - } else if (size) { - if (type == T_SRV) { - PurpleSrvResponse *res; - PurpleSrvResponse *tmp; - PurpleSrvCallback cb = query_data->cb.srv; - ssize_t red; - purple_debug_info("dnssrv","found %d SRV entries\n", size); - tmp = res = g_new0(PurpleSrvResponse, size); - for (i = 0; i < size; i++) { - red = read(source, tmp++, sizeof(PurpleSrvResponse)); - if (red != sizeof(PurpleSrvResponse)) { - purple_debug_error("dnssrv","unable to read srv " - "response: %s\n", g_strerror(errno)); - size = 0; - g_free(res); - res = NULL; - } - } - - cb(res, size, query_data->extradata); - } else if (type == T_TXT) { - GList *responses = NULL; - PurpleTxtResponse *res; - PurpleTxtCallback cb = query_data->cb.txt; - ssize_t red; - purple_debug_info("dnssrv","found %d TXT entries\n", size); - for (i = 0; i < size; i++) { - gsize len; - - red = read(source, &len, sizeof(len)); - if (red != sizeof(len)) { - purple_debug_error("dnssrv","unable to read txt " - "response length: %s\n", g_strerror(errno)); - size = 0; - g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); - g_list_free(responses); - responses = NULL; - break; - } - if (len > MAX_ADDR_RESPONSE_LEN) { - purple_debug_error("dnssrv", "we've read invalid number\n"); - size = 0; - g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); - g_list_free(responses); - responses = NULL; - break; - } - - res = g_new0(PurpleTxtResponse, 1); - res->content = g_new0(gchar, len); - - red = read(source, res->content, len); - if (red < 0 || (gsize)red != len) { - purple_debug_error("dnssrv","unable to read txt " - "response: %s\n", g_strerror(errno)); - size = 0; - purple_txt_response_destroy(res); - g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); - g_list_free(responses); - responses = NULL; - break; - } - responses = g_list_prepend(responses, res); - } - - responses = g_list_reverse(responses); - cb(responses, query_data->extradata); - } else { - purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno); - } - } - } - } - - waitpid(query_data->pid, &status, 0); - purple_srv_txt_query_destroy(query_data); -} - -#else /* _WIN32 */ - -/* The Jabber Server code was inspiration for parts of this. */ - -static gboolean -res_main_thread_cb(gpointer data) -{ - PurpleSrvResponse *srvres = NULL; - PurpleSrvTxtQueryData *query_data = data; - if(query_data->error_message != NULL) { - purple_debug_error("dnssrv", "%s", query_data->error_message); - if (query_data->type == DNS_TYPE_SRV) { - if (query_data->cb.srv) - query_data->cb.srv(srvres, 0, query_data->extradata); - } else if (query_data->type == DNS_TYPE_TXT) { - if (query_data->cb.txt) - query_data->cb.txt(NULL, query_data->extradata); - } - } else { - if (query_data->type == DNS_TYPE_SRV) { - PurpleSrvResponse *srvres_tmp = NULL; - GList *lst = query_data->results; - int size = g_list_length(lst); - - if(query_data->cb.srv && size > 0) - srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); - while (lst) { - PurpleSrvResponse *lstdata = lst->data; - lst = g_list_delete_link(lst, lst); - - if(query_data->cb.srv) - memcpy(srvres_tmp++, lstdata, sizeof(PurpleSrvResponse)); - g_free(lstdata); - } - - query_data->results = NULL; - - purple_debug_info("dnssrv", "found %d SRV entries\n", size); - - if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata); - } else if (query_data->type == DNS_TYPE_TXT) { - GList *lst = query_data->results; - - purple_debug_info("dnssrv", "found %d TXT entries\n", g_list_length(lst)); - - if (query_data->cb.txt) { - query_data->results = NULL; - query_data->cb.txt(lst, query_data->extradata); - } - } else { - purple_debug_error("dnssrv", "unknown query type"); - } - } - - query_data->resolver = NULL; - query_data->handle = 0; - - purple_srv_txt_query_destroy(query_data); - - return FALSE; -} - -static gpointer -res_thread(gpointer data) -{ - PDNS_RECORD dr = NULL; - int type; - DNS_STATUS ds; - PurpleSrvTxtQueryData *query_data = data; - type = query_data->type; - ds = DnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); - if (ds != ERROR_SUCCESS) { - gchar *msg = g_win32_error_message(ds); - if (type == DNS_TYPE_SRV) { - query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); - } else if (type == DNS_TYPE_TXT) { - query_data->error_message = g_strdup_printf("Couldn't look up TXT record. %s (%lu).\n", msg, ds); - } - g_free(msg); - } else { - if (type == DNS_TYPE_SRV) { - PDNS_RECORD dr_tmp; - GList *lst = NULL; - DNS_SRV_DATA *srv_data; - PurpleSrvResponse *srvres; - - for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { - /* Discard any incorrect entries. I'm not sure if this is necessary */ - if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { - continue; - } - - srv_data = &dr_tmp->Data.SRV; - srvres = g_new0(PurpleSrvResponse, 1); - strncpy(srvres->hostname, srv_data->pNameTarget, 255); - srvres->hostname[255] = '\0'; - srvres->pref = srv_data->wPriority; - srvres->port = srv_data->wPort; - srvres->weight = srv_data->wWeight; - - lst = g_list_prepend(lst, srvres); - } - - DnsRecordListFree(dr, DnsFreeRecordList); - query_data->results = purple_srv_sort(lst); - } else if (type == DNS_TYPE_TXT) { - PDNS_RECORD dr_tmp; - GList *lst = NULL; - DNS_TXT_DATA *txt_data; - PurpleTxtResponse *txtres; - - for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { - GString *s; - int i; - - /* Discard any incorrect entries. I'm not sure if this is necessary */ - if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { - continue; - } - - txt_data = &dr_tmp->Data.TXT; - txtres = g_new0(PurpleTxtResponse, 1); - - s = g_string_new(""); - for (i = 0; i < (int)txt_data->dwStringCount; ++i) - s = g_string_append(s, txt_data->pStringArray[i]); - txtres->content = g_string_free(s, FALSE); - - lst = g_list_append(lst, txtres); - } - - DnsRecordListFree(dr, DnsFreeRecordList); - query_data->results = lst; - } else { - - } - } - - /* back to main thread */ - /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ - purple_timeout_add(0, res_main_thread_cb, query_data); - - g_thread_exit(NULL); - return NULL; -} - -#endif - -PurpleSrvTxtQueryData * -purple_srv_resolve(PurpleAccount *account, const char *protocol, - const char *transport, const char *domain, PurpleSrvCallback cb, - gpointer extradata) -{ - char *query; - char *hostname; - PurpleSrvTxtQueryData *query_data; - PurpleProxyType proxy_type; -#ifndef _WIN32 - PurpleSrvInternalQuery internal_query; - int in[2], out[2]; - int pid; -#else - GError* err = NULL; -#endif - - if (!protocol || !*protocol || !transport || !*transport || !domain || !*domain) { - purple_debug_error("dnssrv", "Wrong arguments\n"); - cb(NULL, 0, extradata); - g_return_val_if_reached(NULL); - } - - proxy_type = purple_proxy_info_get_proxy_type( - purple_proxy_get_setup(account)); - if (proxy_type == PURPLE_PROXY_TOR) { - purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.\n"); - cb(NULL, 0, extradata); - return NULL; - } - -#ifdef USE_IDN - if (!dns_str_is_ascii(domain)) { - int ret = purple_network_convert_idn_to_ascii(domain, &hostname); - if (ret != 0) { - purple_debug_error("dnssrv", "IDNA ToASCII failed\n"); - cb(NULL, 0, extradata); - return NULL; - } - } else /* Fallthru is intentional */ -#endif - hostname = g_strdup(domain); - - query = g_strdup_printf("_%s._%s.%s", protocol, transport, hostname); - purple_debug_info("dnssrv","querying SRV record for %s: %s\n", domain, - query); - g_free(hostname); - - query_data = query_data_new(PurpleDnsTypeSrv, query, extradata); - query_data->cb.srv = cb; - - if (purple_srv_txt_query_ui_resolve(query_data)) - { - return query_data; - } - -#ifndef _WIN32 - if(pipe(in) || pipe(out)) { - purple_debug_error("dnssrv", "Could not create pipe\n"); - g_free(query); - g_free(query_data); - cb(NULL, 0, extradata); - return NULL; - } - - /* - * TODO: We should put a cap on the number of forked processes that we - * allow at any given time. If we get too many requests they - * should be put into a queue and handled later. (This is what - * we do for A record lookups.) - */ - pid = fork(); - if (pid == -1) { - purple_debug_error("dnssrv", "Could not create process!\n"); - g_free(query); - g_free(query_data); - cb(NULL, 0, extradata); - return NULL; - } - - /* Child */ - if (pid == 0) - { - g_free(query); - g_free(query_data); - - close(out[0]); - close(in[1]); - resolve(in[0], out[1]); - /* resolve() does not return */ - } - - close(out[1]); - close(in[0]); - - internal_query.type = T_SRV; - strncpy(internal_query.query, query, 255); - internal_query.query[255] = '\0'; - - if (write(in[1], &internal_query, sizeof(internal_query)) < 0) - purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); - - query_data->pid = pid; - query_data->fd_out = out[0]; - query_data->fd_in = in[1]; - query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); - - return query_data; -#else - query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err); - if (query_data->resolver == NULL) { - query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", (err && err->message) ? err->message : ""); - g_error_free(err); - } - else - g_thread_unref(query_data->resolver); - - /* The query isn't going to happen, so finish the SRV lookup now. - * Asynchronously call the callback since stuff may not expect - * the callback to be called before this returns */ - if (query_data->error_message != NULL) - query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); - - return query_data; -#endif -} - -PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, - const char *owner, const char *domain, PurpleTxtCallback cb, - gpointer extradata) -{ - char *query; - char *hostname; - PurpleSrvTxtQueryData *query_data; - PurpleProxyType proxy_type; -#ifndef _WIN32 - PurpleSrvInternalQuery internal_query; - int in[2], out[2]; - int pid; -#else - GError* err = NULL; -#endif - - proxy_type = purple_proxy_info_get_proxy_type( - purple_proxy_get_setup(account)); - if (proxy_type == PURPLE_PROXY_TOR) { - purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.\n"); - cb(NULL, extradata); - return NULL; - } - -#ifdef USE_IDN - if (!dns_str_is_ascii(domain)) { - int ret = purple_network_convert_idn_to_ascii(domain, &hostname); - if (ret != 0) { - purple_debug_error("dnssrv", "IDNA ToASCII failed\n"); - cb(NULL, extradata); - return NULL; - } - } else /* fallthru is intentional */ -#endif - hostname = g_strdup(domain); - - query = g_strdup_printf("%s.%s", owner, hostname); - purple_debug_info("dnssrv","querying TXT record for %s: %s\n", domain, - query); - g_free(hostname); - - query_data = query_data_new(PurpleDnsTypeTxt, query, extradata); - query_data->cb.txt = cb; - - if (purple_srv_txt_query_ui_resolve(query_data)) { - /* query intentionally not freed - */ - return query_data; - } - -#ifndef _WIN32 - if(pipe(in) || pipe(out)) { - purple_debug_error("dnssrv", "Could not create pipe\n"); - g_free(query); - g_free(query_data); - cb(NULL, extradata); - return NULL; - } - - /* - * TODO: We should put a cap on the number of forked processes that we - * allow at any given time. If we get too many requests they - * should be put into a queue and handled later. (This is what - * we do for A record lookups.) - */ - pid = fork(); - if (pid == -1) { - purple_debug_error("dnssrv", "Could not create process!\n"); - g_free(query); - g_free(query_data); - cb(NULL, extradata); - return NULL; - } - - /* Child */ - if (pid == 0) - { - g_free(query); - g_free(query_data); - - close(out[0]); - close(in[1]); - resolve(in[0], out[1]); - /* resolve() does not return */ - } - - close(out[1]); - close(in[0]); - - internal_query.type = T_TXT; - strncpy(internal_query.query, query, 255); - internal_query.query[255] = '\0'; - - if (write(in[1], &internal_query, sizeof(internal_query)) < 0) - purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); - - query_data->pid = pid; - query_data->fd_out = out[0]; - query_data->fd_in = in[1]; - query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); - - return query_data; -#else - query_data->resolver = g_thread_try_new("dnssrv srv resolver", res_thread, query_data, &err); - if (query_data->resolver == NULL) { - query_data->error_message = g_strdup_printf("TXT thread create failure: %s\n", (err && err->message) ? err->message : ""); - g_error_free(err); - } - else - g_thread_unref(query_data->resolver); - - /* The query isn't going to happen, so finish the TXT lookup now. - * Asynchronously call the callback since stuff may not expect - * the callback to be called before this returns */ - if (query_data->error_message != NULL) - query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); - - return query_data; -#endif -} - -const gchar * -purple_txt_response_get_content(PurpleTxtResponse *resp) -{ - g_return_val_if_fail(resp != NULL, NULL); - - return resp->content; -} - -void purple_txt_response_destroy(PurpleTxtResponse *resp) -{ - g_return_if_fail(resp != NULL); - - g_free(resp->content); - g_free(resp); -} - -/* - * Only used as the callback for the ui ops. - */ -static void -purple_srv_query_resolved(PurpleSrvTxtQueryData *query_data, GList *records) -{ - GList *l; - PurpleSrvResponse *records_array; - int i = 0, length; - - g_return_if_fail(records != NULL); - - if (query_data->cb.srv == NULL) { - purple_srv_txt_query_destroy(query_data); - - while (records) { - g_free(records->data); - records = g_list_delete_link(records, records); - } - return; - } - - records = purple_srv_sort(records); - length = g_list_length(records); - - purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n", - query_data->query, length); - - records_array = g_new(PurpleSrvResponse, length); - for (l = records; l; l = l->next, i++) { - records_array[i] = *(PurpleSrvResponse *)l->data; - } - - query_data->cb.srv(records_array, length, query_data->extradata); - - purple_srv_txt_query_destroy(query_data); - - while (records) { - g_free(records->data); - records = g_list_delete_link(records, records); - } -} - -/* - * Only used as the callback for the ui ops. - */ -static void -purple_txt_query_resolved(PurpleSrvTxtQueryData *query_data, GList *entries) -{ - g_return_if_fail(entries != NULL); - - purple_debug_info("dnssrv", "TXT entries resolved for %s, count: %d\n", query_data->query, g_list_length(entries)); - - /* the callback should g_free the entries. - */ - if (query_data->cb.txt != NULL) - query_data->cb.txt(entries, query_data->extradata); - else { - while (entries) { - g_free(entries->data); - entries = g_list_delete_link(entries, entries); - } - } - - purple_srv_txt_query_destroy(query_data); -} - -static void -purple_srv_query_failed(PurpleSrvTxtQueryData *query_data, const gchar *error_message) -{ - purple_debug_error("dnssrv", "%s\n", error_message); - - if (query_data->cb.srv != NULL) - query_data->cb.srv(NULL, 0, query_data->extradata); - - purple_srv_txt_query_destroy(query_data); -} - -static gboolean -purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data) -{ - PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); - - if (ops && ops->resolve) - return ops->resolve(query_data, (query_data->type == T_SRV ? purple_srv_query_resolved : purple_txt_query_resolved), purple_srv_query_failed); - - return FALSE; -} - -void -purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops) -{ - srv_txt_query_ui_ops = ops; -} - -PurpleSrvTxtQueryUiOps * -purple_srv_txt_query_get_ui_ops(void) -{ - /* It is perfectly acceptable for srv_txt_query_ui_ops to be NULL; this just - * means that the default platform-specific implementation will be used. - */ - return srv_txt_query_ui_ops; -} - -char * -purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data) -{ - g_return_val_if_fail(query_data != NULL, NULL); - - return query_data->query; -} - - -int -purple_srv_txt_query_get_query_type(PurpleSrvTxtQueryData *query_data) -{ - g_return_val_if_fail(query_data != NULL, 0); - - return query_data->type; -} - -/************************************************************************** - * GBoxed code - **************************************************************************/ -static PurpleSrvTxtQueryUiOps * -purple_srv_txt_query_ui_ops_copy(PurpleSrvTxtQueryUiOps *ops) -{ - PurpleSrvTxtQueryUiOps *ops_new; - - g_return_val_if_fail(ops != NULL, NULL); - - ops_new = g_new(PurpleSrvTxtQueryUiOps, 1); - *ops_new = *ops; - - return ops_new; -} - -GType -purple_srv_txt_query_ui_ops_get_type(void) -{ - static GType type = 0; - - if (type == 0) { - type = g_boxed_type_register_static("PurpleSrvTxtQueryUiOps", - (GBoxedCopyFunc)purple_srv_txt_query_ui_ops_copy, - (GBoxedFreeFunc)g_free); - } - - return type; -}
--- a/libpurple/dnssrv.h Mon Dec 21 21:51:27 2015 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,213 +0,0 @@ -/* purple - * - * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _PURPLE_DNSSRV_H -#define _PURPLE_DNSSRV_H -/** - * SECTION:dnssrv - * @section_id: libpurple-dnssrv - * @short_description: <filename>dnssrv.h</filename> - * @title: DNS SRV Utilities - */ - -#define PURPLE_TYPE_SRV_TXT_QUERY_UI_OPS (purple_srv_txt_query_ui_ops_get_type()) - -typedef struct _PurpleSrvTxtQueryData PurpleSrvTxtQueryData; -typedef struct _PurpleSrvResponse PurpleSrvResponse; -typedef struct _PurpleTxtResponse PurpleTxtResponse; -typedef struct _PurpleSrvTxtQueryUiOps PurpleSrvTxtQueryUiOps; - -#include <glib.h> -#include <glib-object.h> - -struct _PurpleSrvResponse { - char hostname[256]; - int port; - int weight; - int pref; -}; - -struct _PurpleTxtResponse { - char *content; -}; - -typedef void (*PurpleSrvTxtQueryResolvedCallback) (PurpleSrvTxtQueryData *query_data, GList *records); -typedef void (*PurpleSrvTxtQueryFailedCallback) (PurpleSrvTxtQueryData *query_data, const gchar *error_message); - -/** - * PurpleSrvTxtQueryUiOps: - * @resolve: implemented, return %TRUE if the UI takes responsibility for SRV - * queries. When returning %FALSE, the standard implementation is - * used. These callbacks <emphasis>MUST</emphasis> be called - * asynchronously. - * @destroy: Called just before @query_data is freed; this should cancel any - * further use of @query_data the UI would make. Unneeded if @resolve - * is not implemented. - * - * SRV Request UI operations; UIs should implement this if they want to do SRV - * lookups themselves, rather than relying on the core. - * - * See <link linkend="chapter-ui-ops">List of <literal>UiOps</literal> Structures</link> - */ -struct _PurpleSrvTxtQueryUiOps -{ - gboolean (*resolve)(PurpleSrvTxtQueryData *query_data, - PurpleSrvTxtQueryResolvedCallback resolved_cb, - PurpleSrvTxtQueryFailedCallback failed_cb); - - void (*destroy)(PurpleSrvTxtQueryData *query_data); - - /*< private >*/ - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -/** - * PurpleSrvCallback: - * @resp: An array of PurpleSrvResponse of size results. The array - * is sorted based on the order described in the DNS SRV RFC. - * Users of this API should try each record in resp in order, - * starting at the beginning. - */ -typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data); - -/** - * PurpleTxtCallback: - * @responses: A GList of PurpleTxtResponse objects. - * @data: The extra data passed to purple_txt_resolve. - * - * Callback that returns the data retrieved from a DNS TXT lookup. - */ -typedef void (*PurpleTxtCallback)(GList *responses, gpointer data); - -G_BEGIN_DECLS - -/** - * purple_srv_txt_query_ui_ops_get_type: - * - * Returns: The #GType for the #PurpleSrvTxtQueryUiOps boxed structure. - */ -GType purple_srv_txt_query_ui_ops_get_type(void); - -/** - * purple_srv_resolve: - * @account: The account that the query is being done for (or %NULL) - * @protocol: Name of the protocol (e.g. "sip") - * @transport: Name of the transport ("tcp" or "udp") - * @domain: Domain name to query (e.g. "blubb.com") - * @cb: (scope call): A callback which will be called with the results - * @extradata: Extra data to be passed to the callback - * - * Queries an SRV record. - * - * Returns: %NULL if there was an error, otherwise return a reference to - * a data structure that can be used to cancel the pending - * DNS query, if needed. - */ -PurpleSrvTxtQueryData *purple_srv_resolve(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata); - -/** - * purple_txt_resolve: - * @account: The account that the query is being done for (or %NULL) - * @owner: Name of the protocol (e.g. "_xmppconnect") - * @domain: Domain name to query (e.g. "blubb.com") - * @cb: (scope call): A callback which will be called with the results - * @extradata: Extra data to be passed to the callback - * - * Queries an TXT record. - * - * Returns: %NULL if there was an error, otherwise return a reference to - * a data structure that can be used to cancel the pending - * DNS query, if needed. - */ -PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata); - -/** - * purple_txt_response_get_content: - * @response: The TXT response record - * - * Get the value of the current TXT record. - * - * Returns: The value of the current TXT record. - */ -const gchar *purple_txt_response_get_content(PurpleTxtResponse *response); - -/** - * purple_txt_response_destroy: - * @response: The PurpleTxtResponse to destroy. - * - * Destroy a TXT DNS response object. - */ -void purple_txt_response_destroy(PurpleTxtResponse *response); - -/** - * purple_srv_txt_query_destroy: - * @query_data: The SRV/TXT query to cancel. This data structure - * is freed by this function. - * - * Cancel a SRV/TXT query and destroy the associated data structure. - */ -void purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data); - -/** - * purple_srv_txt_query_set_ui_ops: - * @ops: The UI operations structure. - * - * Sets the UI operations structure to be used when doing a SRV/TXT - * resolve. The UI operations need only be set if the UI wants to - * handle the resolve itself; otherwise, leave it as NULL. - */ -void purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops); - -/** - * purple_srv_txt_query_get_ui_ops: - * - * Returns the UI operations structure to be used when doing a SRV/TXT - * resolve. - * - * Returns: The UI operations structure. - */ -PurpleSrvTxtQueryUiOps *purple_srv_txt_query_get_ui_ops(void); - -/** - * purple_srv_txt_query_get_query: - * @query_data: The SRV/TXT query - * - * Get the query from a PurpleSrvTxtQueryData - * - * Returns: The query. - */ -char *purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data); - -/** - * purple_srv_txt_query_get_query_type: - * @query_data: The query - * - * Get the type from a PurpleSrvTxtQueryData (TXT or SRV) - * - * Returns: The query type. - */ -int purple_srv_txt_query_get_query_type(PurpleSrvTxtQueryData *query_data); - -G_END_DECLS - -#endif /* _PURPLE_DNSSRV_H */ -
--- a/libpurple/glibcompat.h Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/glibcompat.h Tue Dec 22 20:25:45 2015 -0600 @@ -61,65 +61,6 @@ return FALSE; } -#if !GLIB_CHECK_VERSION(2, 32, 0) - -#include <glib-object.h> -#include <string.h> - -#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS -#define G_GNUC_END_IGNORE_DEPRECATIONS - -#define G_SOURCE_REMOVE FALSE -#define G_SOURCE_CONTINUE TRUE - -#define g_signal_handlers_disconnect_by_data(instance, data) \ - g_signal_handlers_disconnect_matched((instance), G_SIGNAL_MATCH_DATA, \ - 0, 0, NULL, NULL, (data)) - -static inline GByteArray * g_byte_array_new_take(guint8 *data, gsize len) -{ - GByteArray *array; - - array = g_byte_array_new(); - g_byte_array_append(array, data, len); - g_free(data); - - return array; -} - -static inline void g_queue_free_full(GQueue *queue, GDestroyNotify free_func) -{ - g_queue_foreach(queue, (GFunc)free_func, NULL); - g_queue_free(queue); -} - -static inline GThread * g_thread_try_new(const gchar *name, GThreadFunc func, - gpointer data, GError **error) -{ - return g_thread_create(func, data, TRUE, error); -} - -#if !GLIB_CHECK_VERSION(2, 30, 0) - -#define G_VALUE_INIT {0, {{0}}} - -static inline gchar *g_utf8_substring(const gchar *str, glong start_pos, - glong end_pos) -{ - gchar *start = g_utf8_offset_to_pointer(str, start_pos); - gchar *end = g_utf8_offset_to_pointer(start, end_pos - start_pos); - gchar *out = g_malloc(end - start + 1); - - memcpy(out, start, end - start); - out[end - start] = 0; - - return out; -} - -#endif /* < 2.30.0 */ - -#endif /* < 2.32.0 */ - #endif /* < 2.36.0 */
--- a/libpurple/network.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/network.c Tue Dec 22 20:25:45 2015 -0600 @@ -21,6 +21,8 @@ #include "internal.h" +#include <gio/gio.h> + #ifndef _WIN32 #include <arpa/nameser.h> #include <resolv.h> @@ -46,7 +48,6 @@ #include "prefs.h" #include "stun.h" #include "upnp.h" -#include "dnsquery.h" #ifdef USE_IDN #include <idna.h> @@ -977,40 +978,26 @@ #endif static void -purple_network_ip_lookup_cb(GSList *hosts, gpointer data, - const char *error_message) -{ - const gchar **ip = (const gchar **) data; +purple_network_ip_lookup_cb(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *addresses = NULL; + GInetAddress *address = NULL; + const gchar **ip_address = (const gchar **)data; - if (error_message) { - purple_debug_error("network", "lookup of IP address failed: %s\n", - error_message); - g_slist_free(hosts); + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), result, &error); + if(error) { + purple_debug_info("network", "lookup of IP address failed: %s\n", error->message); + + g_error_free(error); + return; } - if (hosts && g_slist_next(hosts)) { - common_sockaddr_t *addr = g_slist_next(hosts)->data; - char dst[INET6_ADDRSTRLEN]; + address = G_INET_ADDRESS(addresses->data); - if (addr->sa.sa_family == AF_INET6) { - inet_ntop(addr->sa.sa_family, &addr->in6.sin6_addr, - dst, sizeof(dst)); - } else { - inet_ntop(addr->sa.sa_family, &addr->in.sin_addr, - dst, sizeof(dst)); - } + *ip_address = g_inet_address_to_string(address); - *ip = g_strdup(dst); - purple_debug_info("network", "set IP address: %s\n", *ip); - } - - while (hosts != NULL) { - hosts = g_slist_delete_link(hosts, hosts); - /* Free the address */ - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } + g_resolver_free_addresses(addresses); } void @@ -1018,9 +1005,11 @@ { if (stun_server && stun_server[0] != '\0') { if (purple_network_is_available()) { - purple_debug_info("network", "running DNS query for STUN server\n"); - purple_dnsquery_a(NULL, stun_server, 3478, purple_network_ip_lookup_cb, - &stun_ip); + g_resolver_lookup_by_name_async(g_resolver_get_default(), + stun_server, + NULL, + purple_network_ip_lookup_cb, + &stun_ip); } else { purple_debug_info("network", "network is unavailable, don't try to update STUN IP"); @@ -1036,10 +1025,11 @@ { if (turn_server && turn_server[0] != '\0') { if (purple_network_is_available()) { - purple_debug_info("network", "running DNS query for TURN server\n"); - purple_dnsquery_a(NULL, turn_server, - purple_prefs_get_int("/purple/network/turn_port"), - purple_network_ip_lookup_cb, &turn_ip); + g_resolver_lookup_by_name_async(g_resolver_get_default(), + turn_server, + NULL, + purple_network_ip_lookup_cb, + &turn_server); } else { purple_debug_info("network", "network is unavailable, don't try to update TURN IP");
--- a/libpurple/protocols/gg/resolver-purple.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/gg/resolver-purple.c Tue Dec 22 20:25:45 2015 -0600 @@ -29,22 +29,22 @@ #include <internal.h> #include <debug.h> -#include <dnsquery.h> #include <libgadu.h> #include "resolver-purple.h" +#include <gio/gio.h> + static int ggp_resolver_purple_start(int *fd, void **private_data, const char *hostname); static void ggp_resolver_purple_cleanup(void **private_data, int force); -static void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata, - const char *error_message); +static void ggp_resolver_purple_cb(GObject *sender, GAsyncResult *res, gpointer data); typedef struct { - PurpleDnsQueryData *purpleQuery; + GCancellable *cancellable; /** * File descriptors: @@ -64,67 +64,72 @@ } } -void ggp_resolver_purple_cb(GSList *hosts, gpointer cbdata, - const char *error_message) -{ +void ggp_resolver_purple_cb(GObject *sender, GAsyncResult *res, gpointer cbdata) { + GList *addresses = NULL, *in_addrs = NULL, *l = NULL; + GError *error = NULL; + gsize native_size = 0; /* this is kind of dirty, but it'll be initialized before we use it */ + ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata; const int fd = data->pipes[1]; - int ipv4_count, all_count, write_size; - struct in_addr *addresses; - - purple_debug_misc("gg", "ggp_resolver_purple_cb(%p, %p, \"%s\")\n", - hosts, cbdata, error_message); - data->purpleQuery = NULL; + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), res, &error); + if(addresses == NULL) { + purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n", + error->message); - if (error_message) { - purple_debug_error("gg", "ggp_resolver_purple_cb failed: %s\n", - error_message); + g_error_free(error); + } else { + purple_debug_misc("gg", "ggp_resolver_purple_cb succeeded: (%p, %p)\n", + addresses, cbdata); } - all_count = g_slist_length(hosts); - g_assert(all_count % 2 == 0); - all_count /= 2; - addresses = malloc((all_count + 1) * sizeof(struct in_addr)); + g_object_unref(G_OBJECT(data->cancellable)); + data->cancellable = NULL; - ipv4_count = 0; - while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) { - common_sockaddr_t addr; - char dst[INET6_ADDRSTRLEN]; + for(l = addresses; l; l = l->next) { + GInetAddress *inet_address = G_INET_ADDRESS(l->data); + GSocketFamily family = G_SOCKET_FAMILY_INVALID; + gchar *ip_address = g_inet_address_to_string(inet_address); - memcpy(&addr, hosts->data, sizeof(addr)); + family = g_inet_address_get_family(inet_address); + + switch(family) { + case G_SOCKET_FAMILY_IPV4: + purple_debug_misc("gg", "ggp_resolver_purple_cb " + "ipv4: %s\n", ip_address); - if (addr.sa.sa_family == AF_INET6) { - inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr, - dst, sizeof(dst)); - purple_debug_misc("gg", "ggp_resolver_purple_cb " - "ipv6 (ignore): %s\n", dst); - } else if (addr.sa.sa_family == AF_INET) { - inet_ntop(addr.sa.sa_family, &addr.in.sin_addr, - dst, sizeof(dst)); - purple_debug_misc("gg", "ggp_resolver_purple_cb " - "ipv4: %s\n", dst); + native_size = g_inet_address_get_native_size(inet_address); + in_addrs = g_list_append(in_addrs, g_memdup(g_inet_address_to_bytes(inet_address), native_size)); + + break; + case G_SOCKET_FAMILY_IPV6: + purple_debug_misc("gg", "ggp_resolver_purple_cb " + "ipv6 (ignore): %s\n", ip_address); - g_assert(ipv4_count < all_count); - addresses[ipv4_count++] = addr.in.sin_addr; - } else { - purple_debug_warning("gg", "ggp_resolver_purple_cb " - "unexpected sa_family: %d\n", - addr.sa.sa_family); + break; + default: + purple_debug_warning("gg", "ggp_resolver_purple_cb " + "unexpected sa_family: %d\n", + family); + + break; } - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); + g_free(ip_address); } - addresses[ipv4_count].s_addr = INADDR_NONE; + for(l = in_addrs; l; l = l->next) { + gint write_size = native_size; + if(write(fd, l->data, write_size) != write_size) { + purple_debug_error("gg", + "ggp_resolver_purple_cb write error on %p\n", l->data); + } - write_size = (ipv4_count + 1) * sizeof(struct in_addr); - if (write(fd, addresses, write_size) != write_size) { - purple_debug_error("gg", - "ggp_resolver_purple_cb write error\n"); + g_free(l->data); } - free(addresses); + + g_list_free(in_addrs); + g_resolver_free_addresses(addresses); } int ggp_resolver_purple_start(int *fd, void **private_data, @@ -136,7 +141,7 @@ data = malloc(sizeof(ggp_resolver_purple_data)); *private_data = (void*)data; - data->purpleQuery = NULL; + data->cancellable = NULL; data->pipes[0] = 0; data->pipes[1] = 0; @@ -150,10 +155,15 @@ *fd = data->pipes[0]; /* account and port is unknown in this context */ - data->purpleQuery = purple_dnsquery_a(NULL, hostname, 80, - ggp_resolver_purple_cb, (gpointer)data); + data->cancellable = g_cancellable_new(); - if (!data->purpleQuery) { + g_resolver_lookup_by_name_async(g_resolver_get_default(), + hostname, + data->cancellable, + ggp_resolver_purple_cb, + (gpointer)data); + + if (!data->cancellable) { purple_debug_error("gg", "ggp_resolver_purple_start: " "unable to call purple_dnsquery_a\n"); ggp_resolver_purple_cleanup(private_data, 0); @@ -175,8 +185,12 @@ return; *private_data = NULL; - if (data->purpleQuery) - purple_dnsquery_destroy(data->purpleQuery); + if (G_IS_CANCELLABLE(data->cancellable)) { + g_cancellable_cancel(data->cancellable); + + g_object_unref(G_OBJECT(data->cancellable)); + } + if (data->pipes[0]) close(data->pipes[0]); if (data->pipes[1])
--- a/libpurple/protocols/jabber/disco.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/jabber/disco.c Tue Dec 22 20:25:45 2015 -0600 @@ -419,76 +419,39 @@ } -/* should probably share this code with google.c, or maybe from 2.7.0 - introduce an abstracted hostname -> IP function in dns.c */ static void -jabber_disco_stun_lookup_cb(GSList *hosts, gpointer data, - const char *error_message) -{ +jabber_disco_stun_srv_resolve_cb(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *services = NULL; JabberStream *js = (JabberStream *) data; + gint results = 0; - if (error_message) { - purple_debug_error("jabber", "STUN lookup failed: %s\n", - error_message); - g_slist_free(hosts); - js->stun_query = NULL; + services = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); + + if(error != NULL) { + purple_debug_info("jabber", "Failed to look up a STUN record : %s\n", error->message); + + g_error_free(error); + return; } - if (hosts && g_slist_next(hosts)) { - common_sockaddr_t addr; - char dst[INET6_ADDRSTRLEN]; - int port; + results = g_list_length(services); - memcpy(&addr, g_slist_next(hosts)->data, sizeof(addr)); + purple_debug_info("jabber", "got %d SRV responses for STUN.\n", results); - if (addr.sa.sa_family == AF_INET6) { - inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr, - dst, sizeof(dst)); - port = ntohs(addr.in6.sin6_port); - } else { - inet_ntop(addr.sa.sa_family, &addr.in.sin_addr, - dst, sizeof(dst)); - port = ntohs(addr.in.sin_port); - } + if (results > 0) { + GSrvTarget *target = (GSrvTarget *)services->data; + const gchar *hostname = g_srv_target_get_hostname(target); - g_free(js->stun_ip); - js->stun_ip = g_strdup(dst); - js->stun_port = port; + js->stun_ip = g_strdup(hostname); + js->stun_port = g_srv_target_get_port(target); - purple_debug_info("jabber", "set STUN IP/port address: " - "%s:%d\n", dst, port); - - /* unmark ongoing query */ - js->stun_query = NULL; + purple_debug_info("jabber", "set stun address to %s:%d\n", + hostname, js->stun_port); } - while (hosts != NULL) { - hosts = g_slist_delete_link(hosts, hosts); - /* Free the address */ - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } -} - - -static void -jabber_disco_stun_srv_resolve_cb(PurpleSrvResponse *resp, int results, gpointer data) -{ - JabberStream *js = (JabberStream *) data; - - purple_debug_info("jabber", "got %d SRV responses for STUN.\n", results); - js->srv_query_data = NULL; - - if (results > 0) { - PurpleAccount *account; - purple_debug_info("jabber", "looking up IP for %s:%d\n", - resp[0].hostname, resp[0].port); - account = purple_connection_get_account(js->gc); - js->stun_query = - purple_dnsquery_a(account, resp[0].hostname, resp[0].port, - jabber_disco_stun_lookup_cb, js); - } + g_resolver_free_targets(services); } @@ -552,11 +515,14 @@ } } else if (purple_network_get_stun_ip() == NULL || purple_strequal(purple_network_get_stun_ip(), "")) { - js->srv_query_data = - purple_srv_resolve( - purple_connection_get_account(js->gc), "stun", "udp", - js->user->domain, - jabber_disco_stun_srv_resolve_cb, js); + + g_resolver_lookup_service_async(g_resolver_get_default(), + "stun", + "udp", + js->user->domain, + NULL, + jabber_disco_stun_srv_resolve_cb, + js); /* TODO: add TURN support later... */ } }
--- a/libpurple/protocols/jabber/google/jingleinfo.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/jabber/google/jingleinfo.c Tue Dec 22 20:25:45 2015 -0600 @@ -22,54 +22,36 @@ #include "debug.h" #include "jingleinfo.h" +#include <gio/gio.h> + static void -jabber_google_stun_lookup_cb(GSList *hosts, gpointer data, - const char *error_message) -{ +jabber_google_stun_lookup_cb(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *addresses = NULL; JabberStream *js = (JabberStream *) data; - if (error_message) { + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), result, &error); + + if(error) { purple_debug_error("jabber", "Google STUN lookup failed: %s\n", - error_message); - g_slist_free(hosts); - js->stun_query = NULL; + error->message); + + g_error_free(error); + return; } - if (hosts && g_slist_next(hosts)) { - common_sockaddr_t addr; - char dst[INET6_ADDRSTRLEN]; - int port; - - memcpy(&addr, g_slist_next(hosts)->data, sizeof(addr)); - - if (addr.sa.sa_family == AF_INET6) { - inet_ntop(addr.sa.sa_family, &addr.in6.sin6_addr, - dst, sizeof(dst)); - port = ntohs(addr.in6.sin6_port); - } else { - inet_ntop(addr.sa.sa_family, &addr.in.sin_addr, - dst, sizeof(dst)); - port = ntohs(addr.in.sin_port); - } + if(g_list_length(addresses) > 0) { + GInetAddress *inet_address = G_INET_ADDRESS(addresses->data); g_free(js->stun_ip); - js->stun_ip = g_strdup(dst); - js->stun_port = port; + js->stun_ip = g_inet_address_to_string(inet_address); purple_debug_info("jabber", "set Google STUN IP/port address: " - "%s:%d\n", dst, port); - - /* unmark ongoing query */ - js->stun_query = NULL; + "%s:%d\n", js->stun_ip, js->stun_port); } - while (hosts != NULL) { - hosts = g_slist_delete_link(hosts, hosts); - /* Free the address */ - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } + g_resolver_free_addresses(addresses); } static void @@ -110,16 +92,13 @@ const gchar *udp = purple_xmlnode_get_attrib(server, "udp"); if (host && udp) { - PurpleAccount *account; - int port = atoi(udp); - /* if there, would already be an ongoing query, - cancel it */ - if (js->stun_query) - purple_dnsquery_destroy(js->stun_query); + js->stun_port = atoi(udp); - account = purple_connection_get_account(js->gc); - js->stun_query = purple_dnsquery_a(account, host, port, - jabber_google_stun_lookup_cb, js); + g_resolver_lookup_by_name_async(g_resolver_get_default(), + host, + NULL, + jabber_google_stun_lookup_cb, + js); } } }
--- a/libpurple/protocols/jabber/jabber.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/jabber/jabber.c Tue Dec 22 20:25:45 2015 -0600 @@ -30,7 +30,6 @@ #include "connection.h" #include "conversation.h" #include "debug.h" -#include "dnssrv.h" #include "http.h" #include "message.h" #include "notify.h" @@ -95,7 +94,6 @@ static gint plugin_ref = 0; static void jabber_unregister_account_cb(JabberStream *js); -static void try_srv_connect(JabberStream *js); static void jabber_stream_init(JabberStream *js) { @@ -758,28 +756,54 @@ } static void -txt_resolved_cb(GList *responses, gpointer data) +txt_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *records = NULL, *l = NULL; JabberStream *js = data; gboolean found = FALSE; - js->srv_query_data = NULL; - - while (responses) { - PurpleTxtResponse *resp = responses->data; - gchar **token; - token = g_strsplit(purple_txt_response_get_content(resp), "=", 2); - if (!strcmp(token[0], "_xmpp-client-xbosh")) { - purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); - js->bosh = jabber_bosh_connection_new(js, token[1]); + records = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); + if(error) { + purple_debug_warning("jabber", "Unable to find alternative XMPP connection " + "methods after failing to connect directly. : %s\n", + error->message); + + purple_connection_error(js->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect")); + + g_error_free(error); + + return; + } + + for(l = records; l; l = l->next) { + GVariantIter *iter = NULL; + gchar *str = NULL; + + g_variant_get((GVariant *)l->data, "(as)", &iter); + while(g_variant_iter_loop(iter, "s", &str)) { + gchar **token = g_strsplit(str, "=", 2); + + if(!g_ascii_strcasecmp(token[0], "_xmpp-client-xbosh")) { + purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); + + js->bosh = jabber_bosh_connection_new(js, token[1]); + + g_strfreev(token); + + break; + } + g_strfreev(token); - break; } - g_strfreev(token); - purple_txt_response_destroy(resp); - responses = g_list_delete_link(responses, responses); + + g_variant_iter_free(iter); } + g_list_free_full(records, (GDestroyNotify)g_variant_unref); + if (js->bosh) found = TRUE; @@ -791,11 +815,6 @@ _("Unable to connect")); return; } - - if (responses) { - g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); - g_list_free(responses); - } } static void @@ -805,21 +824,21 @@ JabberStream *js = purple_connection_get_protocol_data(gc); if (source < 0) { - if (js->srv_rec != NULL) { - purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record or connecting directly.\n", error); - try_srv_connect(js); - } else { - purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); - js->srv_query_data = purple_txt_resolve( - purple_connection_get_account(gc), "_xmppconnect", - js->user->domain, txt_resolved_cb, js); - } + gchar *name = g_strdup_printf("_xmppconnect.%s", js->user->domain); + + purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); + + g_resolver_lookup_records_async(g_resolver_get_default(), + name, + G_RESOLVER_RECORD_TXT, + js->cancellable, + txt_resolved_cb, + js); + g_free(name); + return; } - g_free(js->srv_rec); - js->srv_rec = NULL; - js->fd = source; if(js->state == JABBER_STREAM_CONNECTING) @@ -881,37 +900,43 @@ return TRUE; } -static void try_srv_connect(JabberStream *js) +static void +srv_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data) { - while (js->srv_rec != NULL && js->srv_rec_idx < js->max_srv_rec_idx) { - PurpleSrvResponse *tmp_resp = js->srv_rec + (js->srv_rec_idx++); - if (jabber_login_connect(js, tmp_resp->hostname, tmp_resp->hostname, tmp_resp->port, FALSE)) - return; - } - - g_free(js->srv_rec); - js->srv_rec = NULL; - - /* Fall back to the defaults (I'm not sure if we should actually do this) */ - jabber_login_connect(js, js->user->domain, js->user->domain, - purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), - TRUE); -} - -static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data) -{ + GError *error = NULL; + GList *targets = NULL, *l = NULL; JabberStream *js = data; - js->srv_query_data = NULL; - - if(results) { - js->srv_rec = resp; - js->srv_rec_idx = 0; - js->max_srv_rec_idx = results; - try_srv_connect(js); - } else { + + targets = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); + if(error) { + purple_debug_warning("jabber", + "SRV lookup failed, proceeding with normal connection : %s", + error->message); + + g_error_free(error); + jabber_login_connect(js, js->user->domain, js->user->domain, purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), TRUE); + + } else { + for(l = targets; l; l = l->next) { + GSrvTarget *target = (GSrvTarget *)l->data; + const gchar *hostname = g_srv_target_get_hostname(target); + guint port = g_srv_target_get_port(target); + + if(jabber_login_connect(js, hostname, hostname, port, FALSE)) { + g_resolver_free_targets(targets); + + return; + } + } + + g_resolver_free_targets(targets); + + jabber_login_connect(js, js->user->domain, js->user->domain, + purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), + TRUE); } } @@ -930,6 +955,9 @@ js->fd = -1; js->http_conns = purple_http_connection_set_new(); + /* we might want to expose this at some point */ + js->cancellable = g_cancellable_new(); + user = g_strdup(purple_account_get_username(account)); /* jabber_id_new doesn't accept "user@domain/" as valid */ slash = strchr(user, '/'); @@ -1000,7 +1028,6 @@ js->sessions = NULL; js->stun_ip = NULL; js->stun_port = 0; - js->stun_query = NULL; js->google_relay_token = NULL; js->google_relay_host = NULL; @@ -1062,8 +1089,13 @@ jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); } else { - js->srv_query_data = purple_srv_resolve(account, "xmpp-client", - "tcp", js->user->domain, srv_resolved_cb, js); + g_resolver_lookup_service_async(g_resolver_get_default(), + "xmpp-client", + "tcp", + js->user->domain, + js->cancellable, + srv_resolved_cb, + js); } } @@ -1595,9 +1627,6 @@ } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0) jabber_send_raw(js, "</stream:stream>", -1); - if (js->srv_query_data) - purple_srv_txt_query_destroy(js->srv_query_data); - if(js->gsc) { purple_ssl_close(js->gsc); } else if (js->fd > 0) { @@ -1693,17 +1722,10 @@ if (js->conn_close_timeout != 0) purple_timeout_remove(js->conn_close_timeout); - g_free(js->srv_rec); - js->srv_rec = NULL; + g_cancellable_cancel(js->cancellable); + g_object_unref(G_OBJECT(js->cancellable)); g_free(js->stun_ip); - js->stun_ip = NULL; - - /* cancel DNS query for STUN, if one is ongoing */ - if (js->stun_query) { - purple_dnsquery_destroy(js->stun_query); - js->stun_query = NULL; - } /* remove Google relay-related stuff */ g_free(js->google_relay_token);
--- a/libpurple/protocols/jabber/jabber.h Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/jabber/jabber.h Tue Dec 22 20:25:45 2015 -0600 @@ -57,10 +57,10 @@ #include <libxml/parser.h> #include <glib.h> #include <gmodule.h> +#include <gio/gio.h> + #include "circularbuffer.h" #include "connection.h" -#include "dnsquery.h" -#include "dnssrv.h" #include "http.h" #include "media.h" #include "mediamanager.h" @@ -120,7 +120,7 @@ int fd; guint inpa; - PurpleSrvTxtQueryData *srv_query_data; + GCancellable *cancellable; xmlParserCtxt *context; PurpleXmlNode *current; @@ -277,10 +277,6 @@ guint inactivity_timer; guint conn_close_timeout; - PurpleSrvResponse *srv_rec; - guint srv_rec_idx; - guint max_srv_rec_idx; - PurpleJabberBOSHConnection *bosh; PurpleHttpConnectionSet *http_conns; @@ -291,7 +287,6 @@ /* maybe this should only be present when USE_VV? */ gchar *stun_ip; int stun_port; - PurpleDnsQueryData *stun_query; /* stuff for Google's relay handling */ gchar *google_relay_token;
--- a/libpurple/protocols/simple/simple.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/simple/simple.c Tue Dec 22 20:25:45 2015 -0600 @@ -29,7 +29,6 @@ #include "accountopt.h" #include "buddylist.h" #include "conversation.h" -#include "dnsquery.h" #include "debug.h" #include "notify.h" #include "protocol.h" @@ -41,7 +40,6 @@ #include "simple.h" #include "sipmsg.h" -#include "dnssrv.h" #include "ntlm.h" static PurpleProtocol *my_protocol = NULL; @@ -1801,29 +1799,41 @@ do_register(sip); } -static void simple_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { +static void +simple_udp_host_resolved(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *addresses = NULL; + GInetAddress *inet_address = NULL; + GSocketAddress *socket_address = NULL; struct simple_account_data *sip = (struct simple_account_data*) data; - int addr_size; - sip->query_data = NULL; + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), result, &error); + if(error) { + gchar *msg = g_strdup_printf(_("Unable to resolve hostname : %s"), + error->message); - if (!hosts || !hosts->data) { purple_connection_error(sip->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to resolve hostname")); + msg + ); + + g_error_free(error); + return; } - addr_size = GPOINTER_TO_INT(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - memcpy(&(sip->serveraddr), hosts->data, addr_size); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - while(hosts) { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } + inet_address = G_INET_ADDRESS(addresses->data); + socket_address = g_inet_socket_address_new(inet_address, sip->realport); + g_object_unref(G_OBJECT(inet_address)); + + g_socket_address_to_native(socket_address, + &(sip->serveraddr), + g_socket_address_get_native_size(socket_address), + NULL); + + g_object_unref(G_OBJECT(socket_address)); + + g_resolver_free_addresses(addresses); /* create socket for incoming connections */ sip->listen_data = purple_network_listen_range(5060, 5160, AF_UNSPEC, SOCK_DGRAM, TRUE, @@ -1865,33 +1875,44 @@ } } -static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data) { +static void +srvresolved(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *targets = NULL; struct simple_account_data *sip; gchar *hostname; int port; sip = data; - sip->srv_query_data = NULL; - - port = purple_account_get_int(sip->account, "port", 0); - /* find the host to connect to */ - if(results) { - hostname = g_strdup(resp->hostname); - if(!port) - port = resp->port; - g_free(resp); - } else { + targets = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); + if(error) { + purple_debug_info("simple", + "srv lookup failed, continuing with configured settings : %s", + error->message); + + g_error_free(error); + if(!purple_account_get_bool(sip->account, "useproxy", FALSE)) { hostname = g_strdup(sip->servername); } else { hostname = g_strdup(purple_account_get_string(sip->account, "proxy", sip->servername)); - } + } + port = purple_account_get_int(sip->account, "port", 0); + } else { + GSrvTarget *target = (GSrvTarget *)targets->data; + + hostname = g_strdup(g_srv_target_get_hostname(target)); + port = g_srv_target_get_port(target); + + g_resolver_free_targets(targets); } sip->realhostname = hostname; sip->realport = port; - if(!sip->realport) sip->realport = 5060; + + if(!sip->realport) + sip->realport = 5060; /* TCP case */ if(!sip->udp) { @@ -1907,13 +1928,11 @@ } else { /* UDP */ purple_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); - sip->query_data = purple_dnsquery_a(sip->account, hostname, - port, simple_udp_host_resolved, sip); - if (sip->query_data == NULL) { - purple_connection_error(sip->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to resolve hostname")); - } + g_resolver_lookup_by_name_async(g_resolver_get_default(), + sip->realhostname, + sip->cancellable, + simple_udp_host_resolved, + sip); } } @@ -1975,8 +1994,13 @@ hosttoconnect = purple_account_get_string(account, "proxy", sip->servername); } - sip->srv_query_data = purple_srv_resolve(account, "sip", - sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip); + g_resolver_lookup_service_async(g_resolver_get_default(), + "sip", + sip->udp ? "udp" : "tcp", + hosttoconnect, + sip->cancellable, + srvresolved, + sip); } static void simple_close(PurpleConnection *gc) @@ -2008,11 +2032,9 @@ purple_timeout_remove(sip->resendtimeout); if (sip->registertimeout) purple_timeout_remove(sip->registertimeout); - if (sip->query_data != NULL) - purple_dnsquery_destroy(sip->query_data); - if (sip->srv_query_data != NULL) - purple_srv_txt_query_destroy(sip->srv_query_data); + g_cancellable_cancel(sip->cancellable); + g_object_unref(G_OBJECT(sip->cancellable)); if (sip->listen_data != NULL) purple_network_listen_cancel(sip->listen_data);
--- a/libpurple/protocols/simple/simple.h Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/simple/simple.h Tue Dec 22 20:25:45 2015 -0600 @@ -25,12 +25,11 @@ #include <glib.h> #include <gmodule.h> +#include <gio/gio.h> #include <time.h> #include "cipher.h" #include "circularbuffer.h" -#include "dnsquery.h" -#include "dnssrv.h" #include "network.h" #include "proxy.h" #include "protocol.h" @@ -100,8 +99,7 @@ gchar *servername; gchar *username; gchar *password; - PurpleDnsQueryData *query_data; - PurpleSrvTxtQueryData *srv_query_data; + GCancellable *cancellable; PurpleNetworkListenData *listen_data; int fd; int cseq;
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Tue Dec 22 20:25:45 2015 -0600 @@ -23,7 +23,6 @@ /* TODO: it needs further refactoring */ #include "internal.h" -#include "dnsquery.h" #include "protocol.h" #include "util.h" @@ -39,6 +38,8 @@ #include "yahoo_doodle.h" #include "yahoo_friend.h" +#include <gio/gio.h> + struct yahoo_xfer_data { gchar *url; gboolean is_relay; @@ -498,14 +499,15 @@ return ans; } -static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char *error_message) +static void +yahoo_xfer_dns_connected_15(GObject *sender, GAsyncResult *result, gpointer data) { + GError *error = NULL; + GList *addresses = NULL; + GInetAddress *inet_address = NULL; PurpleXfer *xfer; struct yahoo_xfer_data *xd; - struct sockaddr_in *addr; struct yahoo_packet *pkt; - unsigned long actaddr; - unsigned char a,b,c,d; PurpleConnection *gc; PurpleAccount *account; YahooData *yd; @@ -519,47 +521,22 @@ account = purple_connection_get_account(gc); yd = purple_connection_get_protocol_data(gc); - if(!hosts) - { - purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); - purple_xfer_cancel_remote(xfer); - return; - } + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), result, &error); + if(error) { + purple_debug_error("yahoo", + "Unable to find an IP address for relay.msg.yahoo.com : %s\n", + error->message); - /* Discard the length... */ - hosts = g_slist_remove(hosts, hosts->data); - if(!hosts) - { - purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); purple_xfer_cancel_remote(xfer); + g_error_free(error); + return; } - /* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */ - addr = hosts->data; - actaddr = addr->sin_addr.s_addr; - d = actaddr & 0xff; - actaddr >>= 8; - c = actaddr & 0xff; - actaddr >>= 8; - b = actaddr & 0xff; - actaddr >>= 8; - a = actaddr & 0xff; - - xd->host = g_strdup_printf("%u.%u.%u.%u", d, c, b, a); + inet_address = G_INET_ADDRESS(addresses->data); + xd->host = g_inet_address_to_string(inet_address); - /* Free the address... */ - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - addr = NULL; - while (hosts != NULL) - { - /* Discard the length... */ - hosts = g_slist_remove(hosts, hosts->data); - /* Free the address... */ - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } + g_resolver_free_addresses(addresses); pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id); filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); @@ -700,7 +677,6 @@ } if(val_222 == 3) { - PurpleAccount *account; struct yahoo_xfer_data *xd; xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, @@ -726,10 +702,12 @@ } xd->is_relay = TRUE; - account = purple_connection_get_account(gc); - purple_dnsquery_a(account, YAHOO_XFER_RELAY_HOST, - YAHOO_XFER_RELAY_PORT, - yahoo_xfer_dns_connected_15, xfer); + g_resolver_lookup_by_name_async(g_resolver_get_default(), + YAHOO_XFER_RELAY_HOST, + NULL, + yahoo_xfer_dns_connected_15, + xfer); + return; }
--- a/libpurple/proxy.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/proxy.c Tue Dec 22 20:25:45 2015 -0600 @@ -1,4 +1,4 @@ -/* purple + /* purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this @@ -29,7 +29,6 @@ #include "internal.h" #include "ciphers/md5hash.h" #include "debug.h" -#include "dnsquery.h" #include "http.h" #include "notify.h" #include "ntlm.h" @@ -37,6 +36,8 @@ #include "proxy.h" #include "util.h" +#include <gio/gio.h> + struct _PurpleProxyInfo { PurpleProxyType type; /* The proxy type. */ @@ -57,13 +58,14 @@ int socket_type; guint inpa; PurpleProxyInfo *gpi; - PurpleDnsQueryData *query_data; + + GCancellable *cancellable; /* - * This contains alternating length/char* values. The char* - * values need to be freed when removed from the linked list. + * This list contains GInetAddress and they should be freed with + * g_resolver_free_addresses when done with. */ - GSList *hosts; + GList *hosts; PurpleProxyConnectData *child; @@ -574,18 +576,16 @@ handles = g_slist_remove(handles, connect_data); - if (connect_data->query_data != NULL) - purple_dnsquery_destroy(connect_data->query_data); - - while (connect_data->hosts != NULL) - { - /* Discard the length... */ - connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); - /* Free the address... */ - g_free(connect_data->hosts->data); - connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); + if(G_IS_CANCELLABLE(connect_data->cancellable)) { + g_cancellable_cancel(connect_data->cancellable); + + g_object_unref(G_OBJECT(connect_data->cancellable)); + + connect_data->cancellable = NULL; } + g_resolver_free_addresses(connect_data->hosts); + g_free(connect_data->host); g_free(connect_data); } @@ -1307,16 +1307,31 @@ } static void -s4_host_resolved(GSList *hosts, gpointer data, const char *error_message) +s4_host_resolved(GObject *source_object, GAsyncResult *res, gpointer data) { + GResolver *resolver = NULL; + GInetAddress *address = NULL; + GError *error = NULL; + GList *hosts = NULL, *l = NULL; PurpleProxyConnectData *connect_data = data; unsigned char packet[9]; - common_sockaddr_t *addr; - - connect_data->query_data = NULL; - - if (error_message != NULL) { - purple_proxy_connect_data_disconnect(connect_data, error_message); + + if(G_IS_CANCELLABLE(connect_data->cancellable)) { + g_object_unref(G_OBJECT(connect_data->cancellable)); + + connect_data->cancellable = NULL; + } + + resolver = g_resolver_get_default(); + + hosts = g_resolver_lookup_by_name_finish(resolver, res, &error); + g_object_unref(G_OBJECT(resolver)); + + if (error->message != NULL) { + purple_proxy_connect_data_disconnect(connect_data, error->message); + + g_error_free(error); + return; } @@ -1326,37 +1341,37 @@ return; } - /* Discard the length... */ - hosts = g_slist_delete_link(hosts, hosts); - addr = hosts->data; - hosts = g_slist_delete_link(hosts, hosts); - - packet[0] = 0x04; - packet[1] = 0x01; - packet[2] = connect_data->port >> 8; - packet[3] = connect_data->port & 0xff; - memcpy(packet + 4, &addr->in.sin_addr.s_addr, 4); - packet[8] = 0x00; - - g_free(addr); - - /* We could try the other hosts, but hopefully that shouldn't be necessary */ - while (hosts != NULL) { - /* Discard the length... */ - hosts = g_slist_delete_link(hosts, hosts); - /* Free the address... */ - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); + for(l = hosts; l; l = l->next) { + address = G_INET_ADDRESS(l->data); + + if(!g_inet_address_get_is_loopback(address) && !g_inet_address_get_is_link_local(address)) + break; + + address = NULL; } - connect_data->write_buffer = g_memdup(packet, sizeof(packet)); - connect_data->write_buf_len = sizeof(packet); - connect_data->written_len = 0; - connect_data->read_cb = s4_canread; - - connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data); - - proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); + if(address != NULL) { + packet[0] = 0x04; + packet[1] = 0x01; + packet[2] = connect_data->port >> 8; + packet[3] = connect_data->port & 0xff; + memcpy(packet + 4, g_inet_address_to_bytes(address), 4); + packet[8] = 0x00; + + connect_data->write_buffer = g_memdup(packet, sizeof(packet)); + connect_data->write_buf_len = sizeof(packet); + connect_data->written_len = 0; + connect_data->read_cb = s4_canread; + + connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data); + + proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); + } else { + purple_proxy_connect_data_disconnect_formatted(connect_data, + _("Error resolving %s"), connect_data->host); + } + + g_resolver_free_addresses(hosts); } static void @@ -1416,11 +1431,21 @@ proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); } else { - connect_data->query_data = purple_dnsquery_a( - connect_data->account, connect_data->host, - connect_data->port, s4_host_resolved, connect_data); - - if (connect_data->query_data == NULL) { + GResolver *resolver = NULL; + + connect_data->cancellable = g_cancellable_new(); + + resolver = g_resolver_get_default(); + + g_resolver_lookup_by_name_async(resolver, + connect_data->host, + connect_data->cancellable, + s4_host_resolved, + connect_data); + + g_object_unref(G_OBJECT(resolver)); + + if (connect_data->cancellable == NULL) { purple_debug_error("proxy", "dns query failed unexpectedly.\n"); purple_proxy_connect_data_destroy(connect_data); } @@ -2134,83 +2159,98 @@ static void try_connect(PurpleProxyConnectData *connect_data) { - socklen_t addrlen; - common_sockaddr_t *addr; - char ipaddr[INET6_ADDRSTRLEN]; - - addrlen = GPOINTER_TO_INT(connect_data->hosts->data); - connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); - addr = connect_data->hosts->data; - connect_data->hosts = g_slist_remove(connect_data->hosts, connect_data->hosts->data); -#ifdef HAVE_INET_NTOP - if (addr->sa.sa_family == AF_INET) - inet_ntop(addr->sa.sa_family, &addr->in.sin_addr, - ipaddr, sizeof(ipaddr)); - else if (addr->sa.sa_family == AF_INET6) - inet_ntop(addr->sa.sa_family, &addr->in6.sin6_addr, - ipaddr, sizeof(ipaddr)); -#else - memcpy(ipaddr, inet_ntoa(addr->in.sin_addr), sizeof(ipaddr)); -#endif + GInetAddress *address = NULL; + GSocketAddress *socket_address = NULL; + GError *error = NULL; + char *ipaddr; + + common_sockaddr_t addr; + socklen_t addrlen = 0; + + address = G_INET_ADDRESS(connect_data->hosts->data); + ipaddr = g_inet_address_to_string(address); + purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr); + g_free(ipaddr); + + socket_address = g_inet_socket_address_new(address, connect_data->port); + addrlen = g_socket_address_get_native_size(socket_address); + + g_socket_address_to_native(socket_address, &addr, addrlen, &error); + if(error != NULL) { + purple_debug_info("proxy", "failed connnection : %s\n", error->message); + + g_error_free(error); + + return; + } if (connect_data->socket_type == SOCK_DGRAM) { - proxy_connect_udp_none(connect_data, addr, addrlen); - g_free(addr); + proxy_connect_udp_none(connect_data, &addr, addrlen); + return; } switch (purple_proxy_info_get_proxy_type(connect_data->gpi)) { case PURPLE_PROXY_NONE: - proxy_connect_none(connect_data, addr, addrlen); + proxy_connect_none(connect_data, &addr, addrlen); break; case PURPLE_PROXY_HTTP: - proxy_connect_http(connect_data, addr, addrlen); + proxy_connect_http(connect_data, &addr, addrlen); break; case PURPLE_PROXY_SOCKS4: - proxy_connect_socks4(connect_data, addr, addrlen); + proxy_connect_socks4(connect_data, &addr, addrlen); break; case PURPLE_PROXY_SOCKS5: case PURPLE_PROXY_TOR: - proxy_connect_socks5(connect_data, addr, addrlen); + proxy_connect_socks5(connect_data, &addr, addrlen); break; case PURPLE_PROXY_USE_ENVVAR: - proxy_connect_http(connect_data, addr, addrlen); + proxy_connect_http(connect_data, &addr, addrlen); break; default: break; } - g_free(addr); + g_object_unref(G_OBJECT(socket_address)); } static void -connection_host_resolved(GSList *hosts, gpointer data, - const char *error_message) -{ - PurpleProxyConnectData *connect_data; - - connect_data = data; - connect_data->query_data = NULL; - - if (error_message != NULL) - { - purple_proxy_connect_data_disconnect(connect_data, error_message); +connection_host_resolved(GObject *source, GAsyncResult *res, gpointer data) { + PurpleProxyConnectData *connect_data = (PurpleProxyConnectData *)data; + GError *error = NULL; + GList *addresses = NULL; + + addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), res, &error); + + if(G_IS_CANCELLABLE(connect_data->cancellable)) { + g_object_unref(G_OBJECT(connect_data->cancellable)); + + connect_data->cancellable = NULL; + } + + if (error != NULL) { + purple_proxy_connect_data_disconnect(connect_data, error->message); + + g_error_free(error); + + g_resolver_free_addresses(addresses); + return; } - if (hosts == NULL) - { + if (addresses == NULL) { purple_proxy_connect_data_disconnect(connect_data, _("Unable to resolve hostname")); + return; } - connect_data->hosts = hosts; + connect_data->hosts = addresses; try_connect(connect_data); } @@ -2349,9 +2389,15 @@ return NULL; } - connect_data->query_data = purple_dnsquery_a(account, connecthost, - connectport, connection_host_resolved, connect_data); - if (connect_data->query_data == NULL) + connect_data->cancellable = g_cancellable_new(); + + g_resolver_lookup_by_name_async(g_resolver_get_default(), + connecthost, + connect_data->cancellable, + connection_host_resolved, + connect_data); + + if (connect_data->cancellable == NULL) { purple_debug_error("proxy", "dns query failed unexpectedly.\n"); purple_proxy_connect_data_destroy(connect_data); @@ -2420,10 +2466,15 @@ return NULL; } - connect_data->query_data = purple_dnsquery_a(account, connecthost, - connectport, connection_host_resolved, connect_data); - if (connect_data->query_data == NULL) - { + connect_data->cancellable = g_cancellable_new(); + + g_resolver_lookup_by_name_async(g_resolver_get_default(), + connecthost, + connect_data->cancellable, + connection_host_resolved, + connect_data); + + if (connect_data->cancellable == NULL) { purple_proxy_connect_data_destroy(connect_data); return NULL; }
--- a/libpurple/purple.h.in Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/purple.h.in Tue Dec 22 20:25:45 2015 -0600 @@ -62,8 +62,6 @@ #include <core.h> #include <debug.h> #include <desktopitem.h> -#include <dnsquery.h> -#include <dnssrv.h> #include <enums.h> #include <eventloop.h> #include <idle.h>
--- a/libpurple/stun.c Mon Dec 21 21:51:27 2015 -0600 +++ b/libpurple/stun.c Tue Dec 22 20:25:45 2015 -0600 @@ -29,6 +29,8 @@ #include <sys/ioctl.h> #endif +#include <gio/gio.h> + /* Solaris */ #if defined (__SVR4) && defined (__sun) #include <sys/sockio.h> @@ -36,8 +38,6 @@ #include "debug.h" #include "account.h" -#include "dnsquery.h" -#include "dnssrv.h" #include "network.h" #include "proxy.h" #include "stun.h" @@ -82,6 +82,11 @@ size_t packetsize; }; +typedef struct { + gint port; + GList *addresses; +} StunHBNListenData; + static PurpleStunNatDiscovery nattype = { PURPLE_STUN_STATUS_UNDISCOVERED, PURPLE_STUN_NAT_TYPE_PUBLIC_IP, @@ -283,8 +288,11 @@ } -static void hbn_listen_cb(int fd, gpointer data) { - GSList *hosts = data; +static void +hbn_listen_cb(int fd, gpointer data) { + StunHBNListenData *ld = (StunHBNListenData *)data; + GInetAddress *address = NULL; + GSocketAddress *socket_address = NULL; struct stun_conn *sc; static struct stun_header hdr_data; @@ -304,15 +312,15 @@ sc->incb = purple_input_add(fd, PURPLE_INPUT_READ, reply_cb, sc); - hosts = g_slist_delete_link(hosts, hosts); - memcpy(&(sc->addr), hosts->data, sizeof(struct sockaddr_in)); - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - while (hosts) { - hosts = g_slist_delete_link(hosts, hosts); - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } + address = G_INET_ADDRESS(ld->addresses->data); + socket_address = g_inet_socket_address_new(address, ld->port); + + g_socket_address_to_native(socket_address, &(sc->addr), g_socket_address_get_native_size(socket_address), NULL); + + g_object_unref(G_OBJECT(address)); + g_object_unref(G_OBJECT(socket_address)); + g_resolver_free_addresses(ld->addresses); + g_free(ld); hdr_data.type = htons(MSGTYPE_BINDINGREQUEST); hdr_data.len = 0; @@ -336,44 +344,59 @@ sc->timeout = purple_timeout_add(500, (GSourceFunc) timeoutfunc, sc); } -static void hbn_cb(GSList *hosts, gpointer data, const char *error_message) { +static void +hbn_cb(GObject *sender, GAsyncResult *res, gpointer data) { + StunHBNListenData *ld = NULL; + GError *error = NULL; - if(!hosts || !hosts->data) { + ld = g_new0(StunHBNListenData, 1); + + ld->addresses = g_resolver_lookup_by_name_finish(g_resolver_get_default(), res, &error); + if(error != NULL) { nattype.status = PURPLE_STUN_STATUS_UNDISCOVERED; nattype.lookup_time = time(NULL); + do_callbacks(); + return; } - if (!purple_network_listen_range(12108, 12208, AF_UNSPEC, SOCK_DGRAM, TRUE, hbn_listen_cb, hosts)) { - while (hosts) { - hosts = g_slist_delete_link(hosts, hosts); - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } - + if (!purple_network_listen_range(12108, 12208, AF_UNSPEC, SOCK_DGRAM, TRUE, hbn_listen_cb, ld)) { nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); + do_callbacks(); + return; } +} + +static void +do_test1(GObject *sender, GAsyncResult *res, gpointer data) { + GList *services = NULL; + GError *error = NULL; + const char *servername = data; + int port = 3478; + + services = g_resolver_lookup_service_finish(g_resolver_get_default(), res, &error); + if(error != NULL) { + purple_debug_info("stun", "Failed to look up srv record : %s\n", error->message); + + g_error_free(error); + } else { + servername = g_srv_target_get_hostname((GSrvTarget *)services->data); + port = g_srv_target_get_port((GSrvTarget *)services->data); + } - -} - -static void do_test1(PurpleSrvResponse *resp, int results, gpointer sdata) { - const char *servername = sdata; - int port = 3478; + purple_debug_info("stun", "connecting to %s:%d\n", servername, port); - if(results) { - servername = resp[0].hostname; - port = resp[0].port; - } - purple_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", - results, servername, port); + g_resolver_lookup_by_name_async(g_resolver_get_default(), + servername, + NULL, + hbn_cb, + GINT_TO_POINTER(port)); - purple_dnsquery_a(NULL, servername, port, hbn_cb, NULL); - g_free(resp); + g_resolver_free_targets(services); } static gboolean call_callback(gpointer data) { @@ -431,8 +454,14 @@ nattype.servername = g_strdup(servername); callbacks = g_slist_append(callbacks, cb); - purple_srv_resolve(NULL, "stun", "udp", servername, do_test1, - (gpointer) servername); + + g_resolver_lookup_service_async(g_resolver_get_default(), + "stun", + "udp", + servername, + NULL, + do_test1, + (gpointer)servername); return &nattype; }