Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gsrvtarget.c
Line
Count
Source (jump to first uncovered line)
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
 * SECTION:gsrvtarget
35
 * @short_description: DNS SRV record target
36
 * @include: gio/gio.h
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 g_resolver_lookup_service() or
47
 * g_resolver_lookup_service_async() to find the #GSrvTargets
48
 * for a given service. However, if you are simply planning to connect
49
 * to the remote service, you can use #GNetworkService's
50
 * #GSocketConnectable 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
/**
63
 * GSrvTarget:
64
 *
65
 * A single target host/port that a network service is running on.
66
 */
67
68
G_DEFINE_BOXED_TYPE (GSrvTarget, g_srv_target,
69
                     g_srv_target_copy, g_srv_target_free)
70
71
/**
72
 * g_srv_target_new:
73
 * @hostname: the host that the service is running on
74
 * @port: the port that the service is running on
75
 * @priority: the target's priority
76
 * @weight: the target's weight
77
 *
78
 * Creates a new #GSrvTarget with the given parameters.
79
 *
80
 * You should not need to use this; normally #GSrvTargets are
81
 * created by #GResolver.
82
 *
83
 * Returns: a new #GSrvTarget.
84
 *
85
 * Since: 2.22
86
 */
87
GSrvTarget *
88
g_srv_target_new (const gchar *hostname,
89
                  guint16      port,
90
                  guint16      priority,
91
                  guint16      weight)
92
0
{
93
0
  GSrvTarget *target = g_slice_new0 (GSrvTarget);
94
95
0
  target->hostname = g_strdup (hostname);
96
0
  target->port = port;
97
0
  target->priority = priority;
98
0
  target->weight = weight;
99
100
0
  return target;
101
0
}
102
103
/**
104
 * g_srv_target_copy:
105
 * @target: a #GSrvTarget
106
 *
107
 * Copies @target
108
 *
109
 * Returns: a copy of @target
110
 *
111
 * Since: 2.22
112
 */
113
GSrvTarget *
114
g_srv_target_copy (GSrvTarget *target)
115
0
{
116
0
  return g_srv_target_new (target->hostname, target->port,
117
0
                           target->priority, target->weight);
118
0
}
119
120
/**
121
 * g_srv_target_free:
122
 * @target: a #GSrvTarget
123
 *
124
 * Frees @target
125
 *
126
 * Since: 2.22
127
 */
128
void
129
g_srv_target_free (GSrvTarget *target)
130
0
{
131
0
  g_free (target->hostname);
132
0
  g_slice_free (GSrvTarget, target);
133
0
}
134
135
/**
136
 * g_srv_target_get_hostname:
137
 * @target: a #GSrvTarget
138
 *
139
 * Gets @target's hostname (in ASCII form; if you are going to present
140
 * this to the user, you should use g_hostname_is_ascii_encoded() to
141
 * check if it contains encoded Unicode segments, and use
142
 * g_hostname_to_unicode() to convert it if it does.)
143
 *
144
 * Returns: @target's hostname
145
 *
146
 * Since: 2.22
147
 */
148
const gchar *
149
g_srv_target_get_hostname (GSrvTarget *target)
150
0
{
151
0
  return target->hostname;
152
0
}
153
154
/**
155
 * g_srv_target_get_port:
156
 * @target: a #GSrvTarget
157
 *
158
 * Gets @target's port
159
 *
160
 * Returns: @target's port
161
 *
162
 * Since: 2.22
163
 */
164
guint16
165
g_srv_target_get_port (GSrvTarget *target)
166
0
{
167
0
  return target->port;
168
0
}
169
170
/**
171
 * g_srv_target_get_priority:
172
 * @target: a #GSrvTarget
173
 *
174
 * Gets @target's priority. You should not need to look at this;
175
 * #GResolver already sorts the targets according to the algorithm in
176
 * RFC 2782.
177
 *
178
 * Returns: @target's priority
179
 *
180
 * Since: 2.22
181
 */
182
guint16
183
g_srv_target_get_priority (GSrvTarget *target)
184
0
{
185
0
  return target->priority;
186
0
}
187
188
/**
189
 * g_srv_target_get_weight:
190
 * @target: a #GSrvTarget
191
 *
192
 * Gets @target's weight. You should not need to look at this;
193
 * #GResolver already sorts the targets according to the algorithm in
194
 * RFC 2782.
195
 *
196
 * Returns: @target's weight
197
 *
198
 * Since: 2.22
199
 */
200
guint16
201
g_srv_target_get_weight (GSrvTarget *target)
202
0
{
203
0
  return target->weight;
204
0
}
205
206
static gint
207
compare_target (gconstpointer a, gconstpointer b)
208
0
{
209
0
  GSrvTarget *ta = (GSrvTarget *)a;
210
0
  GSrvTarget *tb = (GSrvTarget *)b;
211
212
0
  if (ta->priority == tb->priority)
213
0
    {
214
      /* Arrange targets of the same priority "in any order, except
215
       * that all those with weight 0 are placed at the beginning of
216
       * the list"
217
       */
218
0
      return ta->weight - tb->weight;
219
0
    }
220
0
  else
221
0
    return ta->priority - tb->priority;
222
0
}
223
224
/**
225
 * g_srv_target_list_sort: (skip)
226
 * @targets: a #GList of #GSrvTarget
227
 *
228
 * Sorts @targets in place according to the algorithm in RFC 2782.
229
 *
230
 * Returns: (transfer full): the head of the sorted list.
231
 *
232
 * Since: 2.22
233
 */
234
GList *
235
g_srv_target_list_sort (GList *targets)
236
0
{
237
0
  gint sum, num, val, priority, weight;
238
0
  GList *t, *out, *tail;
239
0
  GSrvTarget *target;
240
241
0
  if (!targets)
242
0
    return NULL;
243
244
0
  if (!targets->next)
245
0
    {
246
0
      target = targets->data;
247
0
      if (!strcmp (target->hostname, "."))
248
0
        {
249
          /* 'A Target of "." means that the service is decidedly not
250
           * available at this domain.'
251
           */
252
0
          g_srv_target_free (target);
253
0
          g_list_free (targets);
254
0
          return NULL;
255
0
        }
256
0
    }
257
258
  /* Sort input list by priority, and put the 0-weight targets first
259
   * in each priority group. Initialize output list to %NULL.
260
   */
261
0
  targets = g_list_sort (targets, compare_target);
262
0
  out = tail = NULL;
263
264
  /* For each group of targets with the same priority, remove them
265
   * from @targets and append them to @out in a valid order.
266
   */
267
0
  while (targets)
268
0
    {
269
0
      priority = ((GSrvTarget *)targets->data)->priority;
270
271
      /* Count the number of targets at this priority level, and
272
       * compute the sum of their weights.
273
       */
274
0
      sum = num = 0;
275
0
      for (t = targets; t; t = t->next)
276
0
        {
277
0
          target = (GSrvTarget *)t->data;
278
0
          if (target->priority != priority)
279
0
            break;
280
0
          sum += target->weight;
281
0
          num++;
282
0
        }
283
284
      /* While there are still targets at this priority level... */
285
0
      while (num)
286
0
        {
287
          /* Randomly select from the targets at this priority level,
288
           * giving precedence to the ones with higher weight,
289
           * according to the rules from RFC 2782.
290
           */
291
0
          val = g_random_int_range (0, sum + 1);
292
0
          for (t = targets; ; t = t->next)
293
0
            {
294
0
              weight = ((GSrvTarget *)t->data)->weight;
295
0
              if (weight >= val)
296
0
                break;
297
0
              val -= weight;
298
0
            }
299
300
0
          targets = g_list_remove_link (targets, t);
301
302
0
          if (!out)
303
0
            out = t;
304
0
          else
305
0
            tail->next = t;
306
0
          tail = t;
307
308
0
          sum -= weight;
309
0
          num--;
310
0
        }
311
0
    }
312
313
0
  return out;
314
0
}