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