mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-05 17:06:18 +01:00
a1ff120a98
scan-build thinks there could be a `NULL` pointer dereference of `t->data` here. It’s wrong, so add an assertion to try and help it understand the control flow. The loop is exited as soon as a target is found whose weight is greater than or equal to a random value between 0 and the sum of all the weights in the set of remaining targets in the loop. By definition, the last target in the loop always satisfies this condition, so a target will always be chosen, and hence `t` will never be `NULL` within the loop. `t->data` will never be `NULL` by construction of the target list. Signed-off-by: Philip Withnall <pwithnall@gnome.org> Helps: #1767
310 lines
7.5 KiB
C
310 lines
7.5 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||
|
||
/* GIO - GLib Input, Output and Streaming Library
|
||
*
|
||
* Copyright (C) 2008 Red Hat, Inc.
|
||
*
|
||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||
*
|
||
* 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.1 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, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include <glib.h>
|
||
#include "glibintl.h"
|
||
|
||
#include "gsrvtarget.h"
|
||
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
|
||
/**
|
||
* GSrvTarget:
|
||
*
|
||
* A single target host/port that a network service is running on.
|
||
*
|
||
* 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.
|
||
*
|
||
* You can use [method@Gio.Resolver.lookup_service] or
|
||
* [method@Gio.Resolver.lookup_service_async] to find the `GSrvTarget`s
|
||
* for a given service. However, if you are simply planning to connect
|
||
* to the remote service, you can use [class@Gio.NetworkService]’s
|
||
* [iface@Gio.SocketConnectable] interface and not need to worry about
|
||
* `GSrvTarget` at all.
|
||
*/
|
||
|
||
struct _GSrvTarget {
|
||
gchar *hostname;
|
||
guint16 port;
|
||
|
||
guint16 priority;
|
||
guint16 weight;
|
||
};
|
||
|
||
G_DEFINE_BOXED_TYPE (GSrvTarget, g_srv_target,
|
||
g_srv_target_copy, g_srv_target_free)
|
||
|
||
/**
|
||
* 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 #GSrvTargets are
|
||
* created by #GResolver.
|
||
*
|
||
* Returns: 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
|
||
*
|
||
* Returns: 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.)
|
||
*
|
||
* Returns: @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
|
||
*
|
||
* Returns: @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.
|
||
*
|
||
* Returns: @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.
|
||
*
|
||
* Returns: @target's weight
|
||
*
|
||
* Since: 2.22
|
||
*/
|
||
guint16
|
||
g_srv_target_get_weight (GSrvTarget *target)
|
||
{
|
||
return target->weight;
|
||
}
|
||
|
||
static 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"
|
||
*/
|
||
return ta->weight - tb->weight;
|
||
}
|
||
else
|
||
return ta->priority - tb->priority;
|
||
}
|
||
|
||
/**
|
||
* g_srv_target_list_sort: (skip)
|
||
* @targets: a #GList of #GSrvTarget
|
||
*
|
||
* Sorts @targets in place according to the algorithm in RFC 2782.
|
||
*
|
||
* Returns: (transfer full): the head of the sorted list.
|
||
*
|
||
* Since: 2.22
|
||
*/
|
||
GList *
|
||
g_srv_target_list_sort (GList *targets)
|
||
{
|
||
gint sum, num, val, priority, weight;
|
||
GList *t, *out, *tail;
|
||
GSrvTarget *target;
|
||
|
||
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 input list by priority, and put the 0-weight targets first
|
||
* in each priority group. Initialize output list to %NULL.
|
||
*/
|
||
targets = g_list_sort (targets, compare_target);
|
||
out = tail = NULL;
|
||
|
||
/* For each group of targets with the same priority, remove them
|
||
* from @targets and append them to @out in a valid order.
|
||
*/
|
||
while (targets)
|
||
{
|
||
priority = ((GSrvTarget *)targets->data)->priority;
|
||
|
||
/* Count the number of targets at this priority level, and
|
||
* compute the sum of their weights.
|
||
*/
|
||
sum = num = 0;
|
||
for (t = targets; t; t = t->next)
|
||
{
|
||
target = (GSrvTarget *)t->data;
|
||
if (target->priority != priority)
|
||
break;
|
||
sum += target->weight;
|
||
num++;
|
||
}
|
||
|
||
/* While there are still targets at this priority level... */
|
||
while (num)
|
||
{
|
||
/* Randomly select from the targets at this priority level,
|
||
* giving precedence to the ones with higher weight,
|
||
* according to the rules from RFC 2782.
|
||
*/
|
||
val = g_random_int_range (0, sum + 1);
|
||
for (t = targets; ; t = t->next)
|
||
{
|
||
g_assert (t != NULL && t->data != NULL);
|
||
weight = ((GSrvTarget *)t->data)->weight;
|
||
if (weight >= val)
|
||
break;
|
||
val -= weight;
|
||
}
|
||
|
||
targets = g_list_remove_link (targets, t);
|
||
|
||
if (!out)
|
||
out = t;
|
||
else
|
||
tail->next = t;
|
||
tail = t;
|
||
|
||
sum -= weight;
|
||
num--;
|
||
}
|
||
}
|
||
|
||
return out;
|
||
}
|