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