docs: update docs
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-address-pool.c
1 /* GStreamer
2  * Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include <string.h>
21 #include <gio/gio.h>
22
23 #include "rtsp-address-pool.h"
24
25 /**
26  * gst_rtsp_address_copy:
27  * @addr: a #GstRTSPAddress
28  *
29  * Make a copy of @addr.
30  *
31  * Returns: a copy of @addr.
32  */
33 GstRTSPAddress *
34 gst_rtsp_address_copy (GstRTSPAddress * addr)
35 {
36   GstRTSPAddress *copy;
37
38   g_return_val_if_fail (addr != NULL, NULL);
39
40   copy = g_slice_dup (GstRTSPAddress, addr);
41   /* only release to the pool when the original is freed. It's a bit
42    * weird but this will do for now as it avoid us to use refcounting. */
43   copy->pool = NULL;
44   copy->address = g_strdup (copy->address);
45
46   return copy;
47 }
48
49 static void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
50     GstRTSPAddress * addr);
51
52 /**
53  * gst_rtsp_address_free:
54  * @addr: a #GstRTSPAddress
55  *
56  * Free @addr and releasing it back into the pool when owned by a
57  * pool.
58  */
59 void
60 gst_rtsp_address_free (GstRTSPAddress * addr)
61 {
62   g_return_if_fail (addr != NULL);
63
64   if (addr->pool) {
65     /* unrefs the pool and sets it to NULL */
66     gst_rtsp_address_pool_release_address (addr->pool, addr);
67   }
68   g_free (addr->address);
69   g_slice_free (GstRTSPAddress, addr);
70 }
71
72 G_DEFINE_BOXED_TYPE (GstRTSPAddress, gst_rtsp_address,
73     (GBoxedCopyFunc) gst_rtsp_address_copy,
74     (GBoxedFreeFunc) gst_rtsp_address_free);
75
76 GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug);
77 #define GST_CAT_DEFAULT rtsp_address_pool_debug
78
79 #define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj)  \
80      (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate))
81
82 struct _GstRTSPAddressPoolPrivate
83 {
84   GMutex lock;                  /* protects everything in this struct */
85   GList *addresses;
86   GList *allocated;
87
88   gboolean has_unicast_addresses;
89 };
90
91 #define ADDR_IS_IPV4(a)      ((a)->size == 4)
92 #define ADDR_IS_IPV6(a)      ((a)->size == 16)
93 #define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0)
94
95 typedef struct
96 {
97   guint8 bytes[16];
98   gsize size;
99   guint16 port;
100 } Addr;
101
102 typedef struct
103 {
104   Addr min;
105   Addr max;
106   guint8 ttl;
107 } AddrRange;
108
109 #define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0)
110
111 #define gst_rtsp_address_pool_parent_class parent_class
112 G_DEFINE_TYPE (GstRTSPAddressPool, gst_rtsp_address_pool, G_TYPE_OBJECT);
113
114 static void gst_rtsp_address_pool_finalize (GObject * obj);
115
116 static void
117 gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass)
118 {
119   GObjectClass *gobject_class;
120
121   gobject_class = G_OBJECT_CLASS (klass);
122
123   gobject_class->finalize = gst_rtsp_address_pool_finalize;
124
125   g_type_class_add_private (klass, sizeof (GstRTSPAddressPoolPrivate));
126
127   GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0,
128       "GstRTSPAddressPool");
129 }
130
131 static void
132 gst_rtsp_address_pool_init (GstRTSPAddressPool * pool)
133 {
134   pool->priv = GST_RTSP_ADDRESS_POOL_GET_PRIVATE (pool);
135
136   g_mutex_init (&pool->priv->lock);
137 }
138
139 static void
140 free_range (AddrRange * range)
141 {
142   g_slice_free (AddrRange, range);
143 }
144
145 static void
146 gst_rtsp_address_pool_finalize (GObject * obj)
147 {
148   GstRTSPAddressPool *pool;
149
150   pool = GST_RTSP_ADDRESS_POOL (obj);
151
152   g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range);
153   g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range);
154   g_mutex_clear (&pool->priv->lock);
155
156   G_OBJECT_CLASS (parent_class)->finalize (obj);
157 }
158
159 /**
160  * gst_rtsp_address_pool_new:
161  *
162  * Make a new #GstRTSPAddressPool.
163  *
164  * Returns: a new #GstRTSPAddressPool
165  */
166 GstRTSPAddressPool *
167 gst_rtsp_address_pool_new (void)
168 {
169   GstRTSPAddressPool *pool;
170
171   pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL);
172
173   return pool;
174 }
175
176 /**
177  * gst_rtsp_address_pool_clear:
178  * @pool: a #GstRTSPAddressPool
179  *
180  * Clear all addresses in @pool. There should be no outstanding
181  * allocations.
182  */
183 void
184 gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool)
185 {
186   GstRTSPAddressPoolPrivate *priv;
187
188   g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
189   g_return_if_fail (pool->priv->allocated == NULL);
190
191   priv = pool->priv;
192
193   g_mutex_lock (&priv->lock);
194   g_list_free_full (priv->addresses, (GDestroyNotify) free_range);
195   priv->addresses = NULL;
196   g_mutex_unlock (&priv->lock);
197 }
198
199 static gboolean
200 fill_address (const gchar * address, guint16 port, Addr * addr,
201     gboolean is_multicast)
202 {
203   GInetAddress *inet;
204
205   inet = g_inet_address_new_from_string (address);
206   if (inet == NULL)
207     return FALSE;
208
209   if (is_multicast != g_inet_address_get_is_multicast (inet)) {
210     g_object_unref (inet);
211     return FALSE;
212   }
213
214   addr->size = g_inet_address_get_native_size (inet);
215   memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size);
216   g_object_unref (inet);
217   addr->port = port;
218
219   return TRUE;
220 }
221
222 static gchar *
223 get_address_string (Addr * addr)
224 {
225   gchar *res;
226   GInetAddress *inet;
227
228   inet = g_inet_address_new_from_bytes (addr->bytes,
229       addr->size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
230   res = g_inet_address_to_string (inet);
231   g_object_unref (inet);
232
233   return res;
234 }
235
236 /**
237  * gst_rtsp_address_pool_add_range:
238  * @pool: a #GstRTSPAddressPool
239  * @min_address: a minimum address to add
240  * @max_address: a maximum address to add
241  * @min_port: the minimum port
242  * @max_port: the maximum port
243  * @ttl: a TTL
244  *
245  * Adds the multicast addresses from @min_addess to @max_address (inclusive)
246  * to @pool. The valid port range for the addresses will be from @min_port to
247  * @max_port inclusive.
248  *
249  * Returns: %TRUE if the addresses could be added.
250  */
251 gboolean
252 gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
253     const gchar * min_address, const gchar * max_address,
254     guint16 min_port, guint16 max_port, guint8 ttl)
255 {
256   AddrRange *range;
257   GstRTSPAddressPoolPrivate *priv;
258
259   g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
260   g_return_val_if_fail (min_port <= max_port, FALSE);
261
262   priv = pool->priv;
263
264   range = g_slice_new0 (AddrRange);
265
266   if (!fill_address (min_address, min_port, &range->min, (ttl != 0)))
267     goto invalid;
268   if (!fill_address (max_address, max_port, &range->max, (ttl != 0)))
269     goto invalid;
270
271   if (range->min.size != range->max.size)
272     goto invalid;
273   if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0)
274     goto invalid;
275
276   range->ttl = ttl;
277
278   GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address,
279       min_port, max_port, ttl);
280
281   g_mutex_lock (&priv->lock);
282   priv->addresses = g_list_prepend (priv->addresses, range);
283
284   if (ttl == 0)
285     priv->has_unicast_addresses = TRUE;
286   g_mutex_unlock (&priv->lock);
287
288   return TRUE;
289
290   /* ERRORS */
291 invalid:
292   {
293     GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address,
294         max_address);
295     g_slice_free (AddrRange, range);
296     return FALSE;
297   }
298 }
299
300 /**
301  * gst_rtsp_address_pool_add_range_unicast:
302  * @pool: a #GstRTSPAddressPool
303  * @min_address: a minimum address to add
304  * @max_address: a maximum address to add
305  * @min_port: the minimum port
306  * @max_port: the maximum port
307  *
308  * Adds the unicast addresses from @min_addess to @max_address (inclusive)
309  * to @pool. The valid port range for the addresses will be from @min_port to
310  * @max_port inclusive.
311  *
312  * @min_address and @max_address can be set to
313  * #GST_RTSP_ADDRESS_POOL_ANY_IPV4 or #GST_RTSP_ADDRESS_POOL_ANY_IPV6 to bind
314  * to all available IPv4 or IPv6 addresses.
315  *
316  * Returns: %TRUE if the addresses could be added.
317  */
318 gboolean
319 gst_rtsp_address_pool_add_range_unicast (GstRTSPAddressPool * pool,
320     const gchar * min_address, const gchar * max_address,
321     guint16 min_port, guint16 max_port)
322 {
323   return gst_rtsp_address_pool_add_range (pool, min_address, max_address,
324       min_port, max_port, 0);
325 }
326
327 /* increments the address by count */
328
329 static void
330 inc_address (Addr * addr, guint8 count)
331 {
332   gint i;
333   guint carry;
334
335   carry = count;
336   for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
337     carry += addr->bytes[i];
338     addr->bytes[i] = carry & 0xff;
339     carry >>= 8;
340   }
341 }
342
343 /* tells us the number of addresses between min_addr and max_addr */
344
345 static guint
346 diff_address (Addr * max_addr, Addr * min_addr)
347 {
348   gint i;
349   guint result = 0;
350
351   g_return_val_if_fail (min_addr->size == max_addr->size, 0);
352
353   for (i = 0; i < min_addr->size; i++) {
354     g_return_val_if_fail (result < (1 << 24), result);
355
356     result <<= 8;
357     result += max_addr->bytes[i] - min_addr->bytes[i];
358   }
359
360   return result;
361 }
362
363
364 static AddrRange *
365 split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr,
366     guint skip_port, gint n_ports)
367 {
368   GstRTSPAddressPoolPrivate *priv = pool->priv;
369   AddrRange *temp;
370
371   if (skip_addr) {
372     temp = g_slice_dup (AddrRange, range);
373     memcpy (temp->max.bytes, temp->min.bytes, temp->min.size);
374     inc_address (&temp->max, skip_addr - 1);
375     priv->addresses = g_list_prepend (priv->addresses, temp);
376
377     inc_address (&range->min, skip_addr);
378   }
379
380   if (!RANGE_IS_SINGLE (range)) {
381     /* min and max are not the same, we have more than one address. */
382     temp = g_slice_dup (AddrRange, range);
383     /* increment the range min address */
384     inc_address (&temp->min, 1);
385     /* and store back in pool */
386     priv->addresses = g_list_prepend (priv->addresses, temp);
387
388     /* adjust range with only the first address */
389     memcpy (range->max.bytes, range->min.bytes, range->min.size);
390   }
391
392   /* range now contains only one single address */
393   if (skip_port > 0) {
394     /* make a range with the skipped ports */
395     temp = g_slice_dup (AddrRange, range);
396     temp->max.port = temp->min.port + skip_port - 1;
397     /* and store back in pool */
398     priv->addresses = g_list_prepend (priv->addresses, temp);
399
400     /* increment range port */
401     range->min.port += skip_port;
402   }
403   /* range now contains single address with desired port number */
404   if (range->max.port - range->min.port + 1 > n_ports) {
405     /* make a range with the remaining ports */
406     temp = g_slice_dup (AddrRange, range);
407     temp->min.port += n_ports;
408     /* and store back in pool */
409     priv->addresses = g_list_prepend (priv->addresses, temp);
410
411     /* and truncate port */
412     range->max.port = range->min.port + n_ports - 1;
413   }
414   return range;
415 }
416
417 /**
418  * gst_rtsp_address_pool_acquire_address:
419  * @pool: a #GstRTSPAddressPool
420  * @flags: flags
421  * @n_ports: the amount of ports
422  *
423  * Take an address and ports from @pool. @flags can be used to control the
424  * allocation. @n_ports consecutive ports will be allocated of which the first
425  * one can be found in @port.
426  *
427  * This function should only be used internally.
428  *
429  * Returns: a #GstRTSPAddress that should be freed with gst_rtsp_address_free
430  *   after use or %NULL when no address could be acquired.
431  */
432 GstRTSPAddress *
433 gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
434     GstRTSPAddressFlags flags, gint n_ports)
435 {
436   GstRTSPAddressPoolPrivate *priv;
437   GList *walk, *next;
438   AddrRange *result;
439   GstRTSPAddress *addr;
440
441   g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
442   g_return_val_if_fail (n_ports > 0, NULL);
443
444   priv = pool->priv;
445   result = NULL;
446   addr = NULL;
447
448   g_mutex_lock (&priv->lock);
449   /* go over available ranges */
450   for (walk = priv->addresses; walk; walk = next) {
451     AddrRange *range;
452     gint ports, skip;
453
454     range = walk->data;
455     next = walk->next;
456
457     /* check address type when given */
458     if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min))
459       continue;
460     if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min))
461       continue;
462     if (flags & GST_RTSP_ADDRESS_FLAG_MULTICAST && range->ttl == 0)
463       continue;
464     if (flags & GST_RTSP_ADDRESS_FLAG_UNICAST && range->ttl != 0)
465       continue;
466
467     /* check for enough ports */
468     ports = range->max.port - range->min.port + 1;
469     if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT
470         && !ADDR_IS_EVEN_PORT (&range->min))
471       skip = 1;
472     else
473       skip = 0;
474     if (ports - skip < n_ports)
475       continue;
476
477     /* we found a range, remove from the list */
478     priv->addresses = g_list_delete_link (priv->addresses, walk);
479     /* now split and exit our loop */
480     result = split_range (pool, range, 0, skip, n_ports);
481     priv->allocated = g_list_prepend (priv->allocated, result);
482     break;
483   }
484   g_mutex_unlock (&priv->lock);
485
486   if (result) {
487     addr = g_slice_new0 (GstRTSPAddress);
488     addr->pool = g_object_ref (pool);
489     addr->address = get_address_string (&result->min);
490     addr->n_ports = n_ports;
491     addr->port = result->min.port;
492     addr->ttl = result->ttl;
493     addr->priv = result;
494
495     GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", addr->address,
496         addr->port, addr->ttl);
497   }
498
499   return addr;
500 }
501
502 /**
503  * gst_rtsp_address_pool_release_address:
504  * @pool: a #GstRTSPAddressPool
505  * @id: an address id
506  *
507  * Release a previously acquired address (with
508  * gst_rtsp_address_pool_acquire_address()) back into @pool.
509  */
510 static void
511 gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
512     GstRTSPAddress * addr)
513 {
514   GstRTSPAddressPoolPrivate *priv;
515   GList *find;
516   AddrRange *range;
517
518   g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
519   g_return_if_fail (addr != NULL);
520   g_return_if_fail (addr->pool == pool);
521
522   priv = pool->priv;
523   range = addr->priv;
524
525   /* we don't want to free twice */
526   addr->priv = NULL;
527   addr->pool = NULL;
528
529   g_mutex_lock (&priv->lock);
530   find = g_list_find (priv->allocated, range);
531   if (find == NULL)
532     goto not_found;
533
534   priv->allocated = g_list_delete_link (priv->allocated, find);
535
536   /* FIXME, merge and do something clever */
537   priv->addresses = g_list_prepend (priv->addresses, range);
538   g_mutex_unlock (&priv->lock);
539
540   g_object_unref (pool);
541
542   return;
543
544   /* ERRORS */
545 not_found:
546   {
547     g_warning ("Released unknown address %p", addr);
548     g_mutex_unlock (&priv->lock);
549     return;
550   }
551 }
552
553 static void
554 dump_range (AddrRange * range, GstRTSPAddressPool * pool)
555 {
556   gchar *addr1, *addr2;
557
558   addr1 = get_address_string (&range->min);
559   addr2 = get_address_string (&range->max);
560   g_print ("  address %s-%s, port %u-%u, ttl %u\n", addr1, addr2,
561       range->min.port, range->max.port, range->ttl);
562   g_free (addr1);
563   g_free (addr2);
564 }
565
566 /**
567  * gst_rtsp_address_pool_dump:
568  * @pool: a #GstRTSPAddressPool
569  *
570  * Dump the free and allocated addresses to stdout.
571  */
572 void
573 gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
574 {
575   GstRTSPAddressPoolPrivate *priv;
576
577   g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
578
579   priv = pool->priv;
580
581   g_mutex_lock (&priv->lock);
582   g_print ("free:\n");
583   g_list_foreach (priv->addresses, (GFunc) dump_range, pool);
584   g_print ("allocated:\n");
585   g_list_foreach (priv->allocated, (GFunc) dump_range, pool);
586   g_mutex_unlock (&priv->lock);
587 }
588
589 /**
590  * gst_rtsp_address_pool_reserve_address:
591  * @pool: a #GstRTSPAddressPool
592  * @address: The IP address to reserve
593  * @port: The first port to reserve
594  * @n_ports: The number of ports
595  * @ttl: The requested ttl
596  *
597  * Take a specific address and ports from @pool. @n_ports consecutive
598  * ports will be allocated of which the first one can be found in
599  * @port.
600  *
601  * This function should only be used internally.
602  *
603  * Returns: a #GstRTSPAddress that should be freed with gst_rtsp_address_free
604  *   after use or %NULL when no address could be acquired.
605  */
606
607 GstRTSPAddress *
608 gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
609     const gchar * address, guint port, guint n_ports, guint ttl)
610 {
611   GstRTSPAddressPoolPrivate *priv;
612   Addr input_addr;
613   GList *walk, *next;
614   AddrRange *result;
615   GstRTSPAddress *addr;
616
617   g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
618   g_return_val_if_fail (address != NULL, NULL);
619   g_return_val_if_fail (port > 0, NULL);
620   g_return_val_if_fail (n_ports > 0, NULL);
621
622   priv = pool->priv;
623   result = NULL;
624   addr = NULL;
625
626   if (!fill_address (address, port, &input_addr, (ttl != 0))) {
627     GST_ERROR_OBJECT (pool, "invalid address %s", address);
628     return NULL;
629   }
630
631   g_mutex_lock (&priv->lock);
632   /* go over available ranges */
633   for (walk = priv->addresses; walk; walk = next) {
634     AddrRange *range;
635     gint skip_port, skip_addr;
636
637     range = walk->data;
638     next = walk->next;
639
640     /* Not the right type of address */
641     if (range->min.size != input_addr.size)
642       continue;
643
644     /* Check that the address is in the interval */
645     if (memcmp (range->min.bytes, input_addr.bytes, input_addr.size) > 0 ||
646         memcmp (range->max.bytes, input_addr.bytes, input_addr.size) < 0)
647       continue;
648
649     /* Make sure the requested ports are inside the range */
650     if (port < range->min.port || port + n_ports - 1 > range->max.port)
651       continue;
652
653     if (ttl != range->ttl)
654       continue;
655
656     skip_addr = diff_address (&input_addr, &range->min);
657     skip_port = port - range->min.port;
658
659     /* we found a range, remove from the list */
660     priv->addresses = g_list_delete_link (priv->addresses, walk);
661     /* now split and exit our loop */
662     result = split_range (pool, range, skip_addr, skip_port, n_ports);
663     priv->allocated = g_list_prepend (priv->allocated, result);
664     break;
665   }
666   g_mutex_unlock (&priv->lock);
667
668   if (result) {
669     addr = g_slice_new0 (GstRTSPAddress);
670     addr->pool = g_object_ref (pool);
671     addr->address = get_address_string (&result->min);
672     addr->n_ports = n_ports;
673     addr->port = result->min.port;
674     addr->ttl = result->ttl;
675     addr->priv = result;
676
677     GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address,
678         addr->port, addr->ttl);
679   }
680
681   return addr;
682 }
683
684 /**
685  * gst_rtsp_address_pool_has_unicast_addresses:
686  * @pool: a #GstRTSPAddressPool
687  *
688  * Used to know if the pool includes any unicast addresses.
689  *
690  * Returns: %TRUE if the pool includes any unicast addresses, %FALSE otherwise
691  */
692
693 gboolean
694 gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool)
695 {
696   GstRTSPAddressPoolPrivate *priv;
697   gboolean has_unicast_addresses;
698
699   g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
700
701   priv = pool->priv;
702
703   g_mutex_lock (&priv->lock);
704   has_unicast_addresses = priv->has_unicast_addresses;
705   g_mutex_unlock (&priv->lock);
706
707   return has_unicast_addresses;
708 }