Documentation and coding style fixups
[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 #include "gioalias.h"
33
34 /**
35  * SECTION:gsrvtarget
36  * @short_description: DNS SRV record target
37  * @include: gio/gio.h
38  *
39  * SRV (service) records are used by some network protocols to provide
40  * service-specific aliasing and load-balancing. For example, XMPP
41  * (Jabber) uses SRV records to locate the XMPP server for a domain;
42  * rather than connecting directly to "example.com" or assuming a
43  * specific server hostname like "xmpp.example.com", an XMPP client
44  * would look up the "xmpp-client" SRV record for "example.com", and
45  * then connect to whatever host was pointed to by that record.
46  *
47  * You can use g_resolver_lookup_service() or
48  * g_resolver_lookup_service_async() to find the #GSrvTarget<!-- -->s
49  * for a given service. However, if you are simply planning to connect
50  * to the remote service, you can use #GNetworkService's
51  * #GSocketConnectable interface and not need to worry about
52  * #GSrvTarget at all.
53  */
54
55 struct _GSrvTarget {
56   gchar   *hostname;
57   guint16  port;
58
59   guint16  priority;
60   guint16  weight;
61 };
62
63 /**
64  * GSrvTarget:
65  *
66  * A single target host/port that a network service is running on.
67  */
68
69 GType
70 g_srv_target_get_type (void)
71 {
72   static volatile gsize type_volatile = 0;
73
74   if (g_once_init_enter (&type_volatile))
75     {
76       GType type = g_boxed_type_register_static (
77                         g_intern_static_string ("GSrvTarget"),
78                         (GBoxedCopyFunc) g_srv_target_copy,
79                         (GBoxedFreeFunc) g_srv_target_free);
80       g_once_init_leave (&type_volatile, type);
81     }
82   return type_volatile;
83 }
84
85 /**
86  * g_srv_target_new:
87  * @hostname: the host that the service is running on
88  * @port: the port that the service is running on
89  * @priority: the target's priority
90  * @weight: the target's weight
91  *
92  * Creates a new #GSrvTarget with the given parameters.
93  *
94  * You should not need to use this; normally #GSrvTarget<!-- -->s are
95  * created by #GResolver.
96  *
97  * Return value: a new #GSrvTarget.
98  *
99  * Since: 2.22
100  */
101 GSrvTarget *
102 g_srv_target_new (const gchar *hostname,
103                   guint16      port,
104                   guint16      priority,
105                   guint16      weight)
106 {
107   GSrvTarget *target = g_slice_new0 (GSrvTarget);
108
109   target->hostname = g_strdup (hostname);
110   target->port = port;
111   target->priority = priority;
112   target->weight = weight;
113
114   return target;
115 }
116
117 /**
118  * g_srv_target_copy:
119  * @target: a #GSrvTarget
120  *
121  * Copies @target
122  *
123  * Return value: a copy of @target
124  *
125  * Since: 2.22
126  */
127 GSrvTarget *
128 g_srv_target_copy (GSrvTarget *target)
129 {
130   return g_srv_target_new (target->hostname, target->port,
131                            target->priority, target->weight);
132 }
133
134 /**
135  * g_srv_target_free:
136  * @target: a #GSrvTarget
137  *
138  * Frees @target
139  *
140  * Since: 2.22
141  */
142 void
143 g_srv_target_free (GSrvTarget *target)
144 {
145   g_free (target->hostname);
146   g_slice_free (GSrvTarget, target);
147 }
148
149 /**
150  * g_srv_target_get_hostname:
151  * @target: a #GSrvTarget
152  *
153  * Gets @target's hostname (in ASCII form; if you are going to present
154  * this to the user, you should use g_hostname_is_ascii_encoded() to
155  * check if it contains encoded Unicode segments, and use
156  * g_hostname_to_unicode() to convert it if it does.)
157  *
158  * Return value: @target's hostname
159  *
160  * Since: 2.22
161  */
162 const gchar *
163 g_srv_target_get_hostname (GSrvTarget *target)
164 {
165   return target->hostname;
166 }
167
168 /**
169  * g_srv_target_get_port:
170  * @target: a #GSrvTarget
171  *
172  * Gets @target's port
173  *
174  * Return value: @target's port
175  *
176  * Since: 2.22
177  */
178 guint16
179 g_srv_target_get_port (GSrvTarget *target)
180 {
181   return target->port;
182 }
183
184 /**
185  * g_srv_target_get_priority:
186  * @target: a #GSrvTarget
187  *
188  * Gets @target's priority. You should not need to look at this;
189  * #GResolver already sorts the targets according to the algorithm in
190  * RFC 2782.
191  *
192  * Return value: @target's priority
193  *
194  * Since: 2.22
195  */
196 guint16
197 g_srv_target_get_priority (GSrvTarget *target)
198 {
199   return target->priority;
200 }
201
202 /**
203  * g_srv_target_get_weight:
204  * @target: a #GSrvTarget
205  *
206  * Gets @target's weight. You should not need to look at this;
207  * #GResolver already sorts the targets according to the algorithm in
208  * RFC 2782.
209  *
210  * Return value: @target's weight
211  *
212  * Since: 2.22
213  */
214 guint16
215 g_srv_target_get_weight (GSrvTarget *target)
216 {
217   return target->weight;
218 }
219
220 gint
221 compare_target (gconstpointer a, gconstpointer b)
222 {
223   GSrvTarget *ta = (GSrvTarget *)a;
224   GSrvTarget *tb = (GSrvTarget *)b;
225
226   if (ta->priority == tb->priority)
227     {
228       /* Arrange targets of the same priority "in any order, except
229        * that all those with weight 0 are placed at the beginning of
230        * the list"
231        */
232       if (ta->weight == 0)
233         return -1;
234       else if (tb->weight == 0)
235         return 1;
236       else
237         return g_random_int_range (-1, 1);
238     }
239   else
240     return ta->priority - tb->priority;
241 }
242
243 /**
244  * g_srv_target_list_sort:
245  * @targets: a #GList of #GSrvTarget
246  *
247  * Sorts @targets in place according to the algorithm in RFC 2782.
248  *
249  * Return value: the head of the sorted list.
250  *
251  * Since: 2.22
252  */
253 GList *
254 g_srv_target_list_sort (GList *targets)
255 {
256   gint sum, val, priority, weight;
257   GList *first, *last, *n;
258   GSrvTarget *target;
259   gpointer tmp;
260
261   if (!targets)
262     return NULL;
263
264   if (!targets->next)
265     {
266       target = targets->data;
267       if (!strcmp (target->hostname, "."))
268         {
269           /* 'A Target of "." means that the service is decidedly not
270            * available at this domain.'
271            */
272           g_srv_target_free (target);
273           g_list_free (targets);
274           return NULL;
275         }
276     }
277
278   /* Sort by priority, and partly by weight */
279   targets = g_list_sort (targets, compare_target);
280
281   /* For each group of targets with the same priority, rebalance them
282    * according to weight.
283    */
284   for (first = targets; first; first = last->next)
285     {
286       /* Skip @first to a non-0-weight target. */
287       while (first && ((GSrvTarget *)first->data)->weight == 0)
288         first = first->next;
289       if (!first)
290         break;
291
292       /* Skip @last to the last target of the same priority. */
293       priority = ((GSrvTarget *)first->data)->priority;
294       last = first;
295       while (last->next &&
296              ((GSrvTarget *)last->next->data)->priority == priority)
297         last = last->next;
298
299       /* If there's only one non-0 weight target at this priority,
300        * we can move on to the next priority level.
301        */
302       if (last == first)
303         continue;
304
305       /* Randomly reorder the non-0 weight targets, giving precedence
306        * to the ones with higher weight. RFC 2782 describes this in
307        * terms of assigning a running sum to each target and building
308        * a new list. We do things slightly differently, but should get
309        * the same result.
310        */
311       for (n = first, sum = 0; n != last->next; n = n->next)
312         sum += ((GSrvTarget *)n->data)->weight;
313       while (first != last)
314         {
315           val = g_random_int_range (0, sum);
316           for (n = first; n != last; n = n->next)
317             {
318               weight = ((GSrvTarget *)n->data)->weight;
319               if (val < weight)
320                 break;
321               val -= weight;
322             }
323
324           tmp = first->data;
325           first->data = n->data;
326           n->data = tmp;
327
328           sum -= weight;
329           first = first->next;
330         }
331     }
332
333   return targets;
334 }
335
336 #define __G_SRV_TARGET_C__
337 #include "gioaliasdef.c"