• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2  
3  /* GIO - GLib Input, Output and Streaming Library
4   *
5   * Copyright (C) 2008 Red Hat, Inc.
6   *
7   * This library is free software; you can redistribute it and/or
8   * modify it under the terms of the GNU Lesser General Public
9   * License as published by the Free Software Foundation; either
10   * version 2.1 of the License, or (at your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General
18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  #include "config.h"
22  #include <glib.h>
23  #include "glibintl.h"
24  
25  #include "gsrvtarget.h"
26  
27  #include <stdlib.h>
28  #include <string.h>
29  
30  
31  /**
32   * SECTION:gsrvtarget
33   * @short_description: DNS SRV record target
34   * @include: gio/gio.h
35   *
36   * SRV (service) records are used by some network protocols to provide
37   * service-specific aliasing and load-balancing. For example, XMPP
38   * (Jabber) uses SRV records to locate the XMPP server for a domain;
39   * rather than connecting directly to "example.com" or assuming a
40   * specific server hostname like "xmpp.example.com", an XMPP client
41   * would look up the "xmpp-client" SRV record for "example.com", and
42   * then connect to whatever host was pointed to by that record.
43   *
44   * You can use g_resolver_lookup_service() or
45   * g_resolver_lookup_service_async() to find the #GSrvTargets
46   * for a given service. However, if you are simply planning to connect
47   * to the remote service, you can use #GNetworkService's
48   * #GSocketConnectable interface and not need to worry about
49   * #GSrvTarget at all.
50   */
51  
52  struct _GSrvTarget {
53    gchar   *hostname;
54    guint16  port;
55  
56    guint16  priority;
57    guint16  weight;
58  };
59  
60  /**
61   * GSrvTarget:
62   *
63   * A single target host/port that a network service is running on.
64   */
65  
G_DEFINE_BOXED_TYPE(GSrvTarget,g_srv_target,g_srv_target_copy,g_srv_target_free)66  G_DEFINE_BOXED_TYPE (GSrvTarget, g_srv_target,
67                       g_srv_target_copy, g_srv_target_free)
68  
69  /**
70   * g_srv_target_new:
71   * @hostname: the host that the service is running on
72   * @port: the port that the service is running on
73   * @priority: the target's priority
74   * @weight: the target's weight
75   *
76   * Creates a new #GSrvTarget with the given parameters.
77   *
78   * You should not need to use this; normally #GSrvTargets are
79   * created by #GResolver.
80   *
81   * Returns: a new #GSrvTarget.
82   *
83   * Since: 2.22
84   */
85  GSrvTarget *
86  g_srv_target_new (const gchar *hostname,
87                    guint16      port,
88                    guint16      priority,
89                    guint16      weight)
90  {
91    GSrvTarget *target = g_slice_new0 (GSrvTarget);
92  
93    target->hostname = g_strdup (hostname);
94    target->port = port;
95    target->priority = priority;
96    target->weight = weight;
97  
98    return target;
99  }
100  
101  /**
102   * g_srv_target_copy:
103   * @target: a #GSrvTarget
104   *
105   * Copies @target
106   *
107   * Returns: a copy of @target
108   *
109   * Since: 2.22
110   */
111  GSrvTarget *
g_srv_target_copy(GSrvTarget * target)112  g_srv_target_copy (GSrvTarget *target)
113  {
114    return g_srv_target_new (target->hostname, target->port,
115                             target->priority, target->weight);
116  }
117  
118  /**
119   * g_srv_target_free:
120   * @target: a #GSrvTarget
121   *
122   * Frees @target
123   *
124   * Since: 2.22
125   */
126  void
g_srv_target_free(GSrvTarget * target)127  g_srv_target_free (GSrvTarget *target)
128  {
129    g_free (target->hostname);
130    g_slice_free (GSrvTarget, target);
131  }
132  
133  /**
134   * g_srv_target_get_hostname:
135   * @target: a #GSrvTarget
136   *
137   * Gets @target's hostname (in ASCII form; if you are going to present
138   * this to the user, you should use g_hostname_is_ascii_encoded() to
139   * check if it contains encoded Unicode segments, and use
140   * g_hostname_to_unicode() to convert it if it does.)
141   *
142   * Returns: @target's hostname
143   *
144   * Since: 2.22
145   */
146  const gchar *
g_srv_target_get_hostname(GSrvTarget * target)147  g_srv_target_get_hostname (GSrvTarget *target)
148  {
149    return target->hostname;
150  }
151  
152  /**
153   * g_srv_target_get_port:
154   * @target: a #GSrvTarget
155   *
156   * Gets @target's port
157   *
158   * Returns: @target's port
159   *
160   * Since: 2.22
161   */
162  guint16
g_srv_target_get_port(GSrvTarget * target)163  g_srv_target_get_port (GSrvTarget *target)
164  {
165    return target->port;
166  }
167  
168  /**
169   * g_srv_target_get_priority:
170   * @target: a #GSrvTarget
171   *
172   * Gets @target's priority. You should not need to look at this;
173   * #GResolver already sorts the targets according to the algorithm in
174   * RFC 2782.
175   *
176   * Returns: @target's priority
177   *
178   * Since: 2.22
179   */
180  guint16
g_srv_target_get_priority(GSrvTarget * target)181  g_srv_target_get_priority (GSrvTarget *target)
182  {
183    return target->priority;
184  }
185  
186  /**
187   * g_srv_target_get_weight:
188   * @target: a #GSrvTarget
189   *
190   * Gets @target's weight. You should not need to look at this;
191   * #GResolver already sorts the targets according to the algorithm in
192   * RFC 2782.
193   *
194   * Returns: @target's weight
195   *
196   * Since: 2.22
197   */
198  guint16
g_srv_target_get_weight(GSrvTarget * target)199  g_srv_target_get_weight (GSrvTarget *target)
200  {
201    return target->weight;
202  }
203  
204  static gint
compare_target(gconstpointer a,gconstpointer b)205  compare_target (gconstpointer a, gconstpointer b)
206  {
207    GSrvTarget *ta = (GSrvTarget *)a;
208    GSrvTarget *tb = (GSrvTarget *)b;
209  
210    if (ta->priority == tb->priority)
211      {
212        /* Arrange targets of the same priority "in any order, except
213         * that all those with weight 0 are placed at the beginning of
214         * the list"
215         */
216        return ta->weight - tb->weight;
217      }
218    else
219      return ta->priority - tb->priority;
220  }
221  
222  /**
223   * g_srv_target_list_sort: (skip)
224   * @targets: a #GList of #GSrvTarget
225   *
226   * Sorts @targets in place according to the algorithm in RFC 2782.
227   *
228   * Returns: (transfer full): the head of the sorted list.
229   *
230   * Since: 2.22
231   */
232  GList *
g_srv_target_list_sort(GList * targets)233  g_srv_target_list_sort (GList *targets)
234  {
235    gint sum, num, val, priority, weight;
236    GList *t, *out, *tail;
237    GSrvTarget *target;
238  
239    if (!targets)
240      return NULL;
241  
242    if (!targets->next)
243      {
244        target = targets->data;
245        if (!strcmp (target->hostname, "."))
246          {
247            /* 'A Target of "." means that the service is decidedly not
248             * available at this domain.'
249             */
250            g_srv_target_free (target);
251            g_list_free (targets);
252            return NULL;
253          }
254      }
255  
256    /* Sort input list by priority, and put the 0-weight targets first
257     * in each priority group. Initialize output list to %NULL.
258     */
259    targets = g_list_sort (targets, compare_target);
260    out = tail = NULL;
261  
262    /* For each group of targets with the same priority, remove them
263     * from @targets and append them to @out in a valid order.
264     */
265    while (targets)
266      {
267        priority = ((GSrvTarget *)targets->data)->priority;
268  
269        /* Count the number of targets at this priority level, and
270         * compute the sum of their weights.
271         */
272        sum = num = 0;
273        for (t = targets; t; t = t->next)
274          {
275            target = (GSrvTarget *)t->data;
276            if (target->priority != priority)
277              break;
278            sum += target->weight;
279            num++;
280          }
281  
282        /* While there are still targets at this priority level... */
283        while (num)
284          {
285            /* Randomly select from the targets at this priority level,
286             * giving precedence to the ones with higher weight,
287             * according to the rules from RFC 2782.
288             */
289            val = g_random_int_range (0, sum + 1);
290            for (t = targets; ; t = t->next)
291              {
292                weight = ((GSrvTarget *)t->data)->weight;
293                if (weight >= val)
294                  break;
295                val -= weight;
296              }
297  
298            targets = g_list_remove_link (targets, t);
299  
300            if (!out)
301              out = t;
302            else
303              tail->next = t;
304            tail = t;
305  
306            sum -= weight;
307            num--;
308          }
309      }
310  
311    return out;
312  }
313