Branch data Line data Source code
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 : 4 : 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 : 6 : g_srv_target_new (const gchar *hostname,
83 : : guint16 port,
84 : : guint16 priority,
85 : : guint16 weight)
86 : : {
87 : 6 : GSrvTarget *target = g_slice_new0 (GSrvTarget);
88 : :
89 : 6 : target->hostname = g_strdup (hostname);
90 : 6 : target->port = port;
91 : 6 : target->priority = priority;
92 : 6 : target->weight = weight;
93 : :
94 : 6 : 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 : 0 : g_srv_target_copy (GSrvTarget *target)
109 : : {
110 : 0 : return g_srv_target_new (target->hostname, target->port,
111 : 0 : 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 : 6 : g_srv_target_free (GSrvTarget *target)
124 : : {
125 : 6 : g_free (target->hostname);
126 : 6 : g_slice_free (GSrvTarget, target);
127 : 6 : }
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 : 1500000 : g_srv_target_get_hostname (GSrvTarget *target)
144 : : {
145 : 1500000 : 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 : 0 : g_srv_target_get_port (GSrvTarget *target)
160 : : {
161 : 0 : 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 : 0 : g_srv_target_get_priority (GSrvTarget *target)
178 : : {
179 : 0 : 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 : 0 : g_srv_target_get_weight (GSrvTarget *target)
196 : : {
197 : 0 : return target->weight;
198 : : }
199 : :
200 : : static gint
201 : 2250000 : compare_target (gconstpointer a, gconstpointer b)
202 : : {
203 : 2250000 : GSrvTarget *ta = (GSrvTarget *)a;
204 : 2250000 : GSrvTarget *tb = (GSrvTarget *)b;
205 : :
206 : 2250000 : 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 : 1250000 : return ta->weight - tb->weight;
213 : : }
214 : : else
215 : 1000000 : 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 : 250000 : 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 : 250000 : if (!targets)
236 : 0 : return NULL;
237 : :
238 : 250000 : if (!targets->next)
239 : : {
240 : 0 : target = targets->data;
241 : 0 : if (!strcmp (target->hostname, "."))
242 : : {
243 : : /* 'A Target of "." means that the service is decidedly not
244 : : * available at this domain.'
245 : : */
246 : 0 : g_srv_target_free (target);
247 : 0 : g_list_free (targets);
248 : 0 : 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 : 250000 : targets = g_list_sort (targets, compare_target);
256 : 250000 : 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 : 750000 : while (targets)
262 : : {
263 : 500000 : 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 : 500000 : sum = num = 0;
269 : 2000000 : for (t = targets; t; t = t->next)
270 : : {
271 : 1750000 : target = (GSrvTarget *)t->data;
272 : 1750000 : if (target->priority != priority)
273 : 250000 : break;
274 : 1500000 : sum += target->weight;
275 : 1500000 : num++;
276 : : }
277 : :
278 : : /* While there are still targets at this priority level... */
279 : 2000000 : 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 : 1500000 : val = g_random_int_range (0, sum + 1);
286 : 1500000 : for (t = targets; ; t = t->next)
287 : : {
288 : 2917234 : g_assert (t != NULL && t->data != NULL);
289 : 2917234 : weight = ((GSrvTarget *)t->data)->weight;
290 : 2917234 : if (weight >= val)
291 : 1500000 : break;
292 : 1417234 : val -= weight;
293 : : }
294 : :
295 : 1500000 : targets = g_list_remove_link (targets, t);
296 : :
297 : 1500000 : if (!out)
298 : 250000 : out = t;
299 : : else
300 : 1250000 : tail->next = t;
301 : 1500000 : tail = t;
302 : :
303 : 1500000 : sum -= weight;
304 : 1500000 : num--;
305 : : }
306 : : }
307 : :
308 : 250000 : return out;
309 : : }
|