summaryrefslogtreecommitdiff
path: root/gio/gsrvtarget.c
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2008-12-29 12:53:47 -0500
committerDan Winship <danw@gnome.org>2009-04-22 08:36:32 -0400
commitc94d3f92885456e1dc9e2fb27b709017f29d04ce (patch)
tree76fd01f40374c14823a8d7fef4cd957bfbab4d8c /gio/gsrvtarget.c
parent68fc0556275edf6e63a3242841f2981a42ee11cb (diff)
downloadglib-c94d3f92885456e1dc9e2fb27b709017f29d04ce.tar.gz
Add GResolver, a glib-ish interface to DNS
GResolver provides asynchronous (and synchronous-but-cancellable) APIs for resolving hostnames, reverse-resolving IP addresses back to hostnames, and resolving SRV records. Part of #548466.
Diffstat (limited to 'gio/gsrvtarget.c')
-rw-r--r--gio/gsrvtarget.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/gio/gsrvtarget.c b/gio/gsrvtarget.c
new file mode 100644
index 000000000..537d81660
--- /dev/null
+++ b/gio/gsrvtarget.c
@@ -0,0 +1,334 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include "glibintl.h"
+
+#include "gsrvtarget.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gsrvtarget
+ * @short_description: DNS SRV record target
+ * @include: gio/gio.h
+ *
+ * SRV (service) records are used by some network protocols to provide
+ * service-specific aliasing and load-balancing. For example, XMPP
+ * (Jabber) uses SRV records to locate the XMPP server for a domain;
+ * rather than connecting directly to "example.com" or assuming a
+ * specific server hostname like "xmpp.example.com", an XMPP client
+ * would look up the "xmpp-client" SRV record for "example.com", and
+ * then connect to whatever host was pointed to by that record.
+ *
+ * Use g_resolver_lookup_service() or
+ * g_resolver_lookup_service_async() to find the #GSrvTarget<!-- -->s
+ * for a given service.
+ **/
+
+struct _GSrvTarget {
+ gchar *hostname;
+ guint16 port;
+
+ guint16 priority;
+ guint16 weight;
+};
+
+/**
+ * GSrvTarget:
+ *
+ * A single target host/port that a network service is running on.
+ */
+
+GType
+g_srv_target_get_type (void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter (&type_volatile))
+ {
+ GType type = g_boxed_type_register_static (
+ g_intern_static_string ("GSrvTarget"),
+ (GBoxedCopyFunc) g_srv_target_copy,
+ (GBoxedFreeFunc) g_srv_target_free);
+ g_once_init_leave (&type_volatile, type);
+ }
+ return type_volatile;
+}
+
+/**
+ * g_srv_target_new:
+ * @hostname: the host that the service is running on
+ * @port: the port that the service is running on
+ * @priority: the target's priority
+ * @weight: the target's weight
+ *
+ * Creates a new #GSrvTarget with the given parameters.
+ *
+ * You should not need to use this; normally #GSrvTarget<!-- -->s are
+ * created by #GResolver.
+ *
+ * Return value: a new #GSrvTarget.
+ *
+ * Since: 2.22
+ **/
+GSrvTarget *
+g_srv_target_new (const gchar *hostname,
+ guint16 port,
+ guint16 priority,
+ guint16 weight)
+{
+ GSrvTarget *target = g_slice_new0 (GSrvTarget);
+
+ target->hostname = g_strdup (hostname);
+ target->port = port;
+ target->priority = priority;
+ target->weight = weight;
+
+ return target;
+}
+
+/**
+ * g_srv_target_copy:
+ * @target: a #GSrvTarget
+ *
+ * Copies @target
+ *
+ * Return value: a copy of @target
+ *
+ * Since: 2.22
+ **/
+GSrvTarget *
+g_srv_target_copy (GSrvTarget *target)
+{
+ return g_srv_target_new (target->hostname, target->port,
+ target->priority, target->weight);
+}
+
+/**
+ * g_srv_target_free:
+ * @target: a #GSrvTarget
+ *
+ * Frees @target
+ *
+ * Since: 2.22
+ **/
+void
+g_srv_target_free (GSrvTarget *target)
+{
+ g_free (target->hostname);
+ g_slice_free (GSrvTarget, target);
+}
+
+/**
+ * g_srv_target_get_hostname:
+ * @target: a #GSrvTarget
+ *
+ * Gets @target's hostname (in ASCII form; if you are going to present
+ * this to the user, you should use g_hostname_is_ascii_encoded() to
+ * check if it contains encoded Unicode segments, and use
+ * g_hostname_to_unicode() to convert it if it does.)
+ *
+ * Return value: @target's hostname
+ *
+ * Since: 2.22
+ **/
+const gchar *
+g_srv_target_get_hostname (GSrvTarget *target)
+{
+ return target->hostname;
+}
+
+/**
+ * g_srv_target_get_port:
+ * @target: a #GSrvTarget
+ *
+ * Gets @target's port
+ *
+ * Return value: @target's port
+ *
+ * Since: 2.22
+ **/
+guint16
+g_srv_target_get_port (GSrvTarget *target)
+{
+ return target->port;
+}
+
+/**
+ * g_srv_target_get_priority:
+ * @target: a #GSrvTarget
+ *
+ * Gets @target's priority. You should not need to look at this;
+ * #GResolver already sorts the targets according to the algorithm in
+ * RFC 2782.
+ *
+ * Return value: @target's priority
+ *
+ * Since: 2.22
+ **/
+guint16
+g_srv_target_get_priority (GSrvTarget *target)
+{
+ return target->priority;
+}
+
+/**
+ * g_srv_target_get_weight:
+ * @target: a #GSrvTarget
+ *
+ * Gets @target's weight. You should not need to look at this;
+ * #GResolver already sorts the targets according to the algorithm in
+ * RFC 2782.
+ *
+ * Return value: @target's weight
+ *
+ * Since: 2.22
+ **/
+guint16
+g_srv_target_get_weight (GSrvTarget *target)
+{
+ return target->weight;
+}
+
+gint
+compare_target (gconstpointer a, gconstpointer b)
+{
+ GSrvTarget *ta = (GSrvTarget *)a;
+ GSrvTarget *tb = (GSrvTarget *)b;
+
+ if (ta->priority == tb->priority)
+ {
+ /* Arrange targets of the same priority "in any order, except
+ * that all those with weight 0 are placed at the beginning of
+ * the list"
+ */
+ if (ta->weight == 0)
+ return -1;
+ else if (tb->weight == 0)
+ return 1;
+ else
+ return g_random_int_range (-1, 1);
+ }
+ else
+ return ta->priority - tb->priority;
+}
+
+/**
+ * g_srv_target_list_sort:
+ * @targets: a #GList of #GSrvTarget
+ *
+ * Sorts @targets in place according to the algorithm in RFC 2782.
+ *
+ * Return value: the head of the sorted list.
+ *
+ * Since: 2.22
+ **/
+GList *
+g_srv_target_list_sort (GList *targets)
+{
+ gint sum, val, priority, weight;
+ GList *first, *last, *n;
+ GSrvTarget *target;
+ gpointer tmp;
+
+ if (!targets)
+ return NULL;
+
+ if (!targets->next)
+ {
+ target = targets->data;
+ if (!strcmp (target->hostname, "."))
+ {
+ /* 'A Target of "." means that the service is decidedly not
+ * available at this domain.'
+ */
+ g_srv_target_free (target);
+ g_list_free (targets);
+ return NULL;
+ }
+ }
+
+ /* Sort by priority, and partly by weight */
+ targets = g_list_sort (targets, compare_target);
+
+ /* For each group of targets with the same priority, rebalance them
+ * according to weight.
+ */
+ for (first = targets; first; first = last->next)
+ {
+ /* Skip @first to a non-0-weight target. */
+ while (first && ((GSrvTarget *)first->data)->weight == 0)
+ first = first->next;
+ if (!first)
+ break;
+
+ /* Skip @last to the last target of the same priority. */
+ priority = ((GSrvTarget *)first->data)->priority;
+ last = first;
+ while (last->next &&
+ ((GSrvTarget *)last->next->data)->priority == priority)
+ last = last->next;
+
+ /* If there's only one non-0 weight target at this priority,
+ * we can move on to the next priority level.
+ */
+ if (last == first)
+ continue;
+
+ /* Randomly reorder the non-0 weight targets, giving precedence
+ * to the ones with higher weight. RFC 2782 describes this in
+ * terms of assigning a running sum to each target and building
+ * a new list. We do things slightly differently, but should get
+ * the same result.
+ */
+ for (n = first, sum = 0; n != last->next; n = n->next)
+ sum += ((GSrvTarget *)n->data)->weight;
+ while (first != last)
+ {
+ val = g_random_int_range (0, sum);
+ for (n = first; n != last; n = n->next)
+ {
+ weight = ((GSrvTarget *)n->data)->weight;
+ if (val < weight)
+ break;
+ val -= weight;
+ }
+
+ tmp = first->data;
+ first->data = n->data;
+ n->data = tmp;
+
+ sum -= weight;
+ first = first->next;
+ }
+ }
+
+ return targets;
+}
+
+#define __G_SRV_TARGET_C__
+#include "gioaliasdef.c"