Coverage Report

Created: 2025-07-01 07:09

/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
 * 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
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
0
{
91
0
  GSrvTarget *target = g_slice_new0 (GSrvTarget);
92
93
0
  target->hostname = g_strdup (hostname);
94
0
  target->port = port;
95
0
  target->priority = priority;
96
0
  target->weight = weight;
97
98
0
  return target;
99
0
}
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 *
112
g_srv_target_copy (GSrvTarget *target)
113
0
{
114
0
  return g_srv_target_new (target->hostname, target->port,
115
0
                           target->priority, target->weight);
116
0
}
117
118
/**
119
 * g_srv_target_free:
120
 * @target: a #GSrvTarget
121
 *
122
 * Frees @target
123
 *
124
 * Since: 2.22
125
 */
126
void
127
g_srv_target_free (GSrvTarget *target)
128
0
{
129
0
  g_free (target->hostname);
130
0
  g_slice_free (GSrvTarget, target);
131
0
}
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 *
147
g_srv_target_get_hostname (GSrvTarget *target)
148
0
{
149
0
  return target->hostname;
150
0
}
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
163
g_srv_target_get_port (GSrvTarget *target)
164
0
{
165
0
  return target->port;
166
0
}
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
181
g_srv_target_get_priority (GSrvTarget *target)
182
0
{
183
0
  return target->priority;
184
0
}
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
199
g_srv_target_get_weight (GSrvTarget *target)
200
0
{
201
0
  return target->weight;
202
0
}
203
204
static gint
205
compare_target (gconstpointer a, gconstpointer b)
206
0
{
207
0
  GSrvTarget *ta = (GSrvTarget *)a;
208
0
  GSrvTarget *tb = (GSrvTarget *)b;
209
210
0
  if (ta->priority == tb->priority)
211
0
    {
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
0
      return ta->weight - tb->weight;
217
0
    }
218
0
  else
219
0
    return ta->priority - tb->priority;
220
0
}
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 *
233
g_srv_target_list_sort (GList *targets)
234
0
{
235
0
  gint sum, num, val, priority, weight;
236
0
  GList *t, *out, *tail;
237
0
  GSrvTarget *target;
238
239
0
  if (!targets)
240
0
    return NULL;
241
242
0
  if (!targets->next)
243
0
    {
244
0
      target = targets->data;
245
0
      if (!strcmp (target->hostname, "."))
246
0
        {
247
          /* 'A Target of "." means that the service is decidedly not
248
           * available at this domain.'
249
           */
250
0
          g_srv_target_free (target);
251
0
          g_list_free (targets);
252
0
          return NULL;
253
0
        }
254
0
    }
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
0
  targets = g_list_sort (targets, compare_target);
260
0
  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
0
  while (targets)
266
0
    {
267
0
      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
0
      sum = num = 0;
273
0
      for (t = targets; t; t = t->next)
274
0
        {
275
0
          target = (GSrvTarget *)t->data;
276
0
          if (target->priority != priority)
277
0
            break;
278
0
          sum += target->weight;
279
0
          num++;
280
0
        }
281
282
      /* While there are still targets at this priority level... */
283
0
      while (num)
284
0
        {
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
0
          val = g_random_int_range (0, sum + 1);
290
0
          for (t = targets; ; t = t->next)
291
0
            {
292
0
              weight = ((GSrvTarget *)t->data)->weight;
293
0
              if (weight >= val)
294
0
                break;
295
0
              val -= weight;
296
0
            }
297
298
0
          targets = g_list_remove_link (targets, t);
299
300
0
          if (!out)
301
0
            out = t;
302
0
          else
303
0
            tail->next = t;
304
0
          tail = t;
305
306
0
          sum -= weight;
307
0
          num--;
308
0
        }
309
0
    }
310
311
0
  return out;
312
0
}