2 * Copyright (C) 2012 Wim Taymans <wim.taymans at gmail.com>
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.
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.
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.
20 * SECTION:rtsp-address-pool
21 * @short_description: A pool of network addresses
22 * @see_also: #GstRTSPStream, #GstRTSPStreamTransport
24 * The #GstRTSPAddressPool is an object that maintains a collection of network
25 * addresses. It is used to allocate server ports and server multicast addresses
26 * but also to reserve client provided destination addresses.
28 * A range of addresses can be added with gst_rtsp_address_pool_add_range().
29 * Both multicast and unicast addresses can be added.
31 * With gst_rtsp_address_pool_acquire_address() an unused address and port range
32 * can be acquired from the pool. With gst_rtsp_address_pool_reserve_address() a
33 * specific address can be retrieved. Both methods return a boxed
34 * #GstRTSPAddress that should be freed with gst_rtsp_address_free() after
35 * usage, which brings the address back into the pool.
37 * Last reviewed on 2013-07-16 (1.0.0)
43 #include "rtsp-address-pool.h"
46 * gst_rtsp_address_copy:
47 * @addr: a #GstRTSPAddress
49 * Make a copy of @addr.
51 * Returns: a copy of @addr.
54 gst_rtsp_address_copy (GstRTSPAddress * addr)
58 g_return_val_if_fail (addr != NULL, NULL);
60 copy = g_slice_dup (GstRTSPAddress, addr);
61 /* only release to the pool when the original is freed. It's a bit
62 * weird but this will do for now as it avoid us to use refcounting. */
64 copy->address = g_strdup (copy->address);
69 static void gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
70 GstRTSPAddress * addr);
73 * gst_rtsp_address_free:
74 * @addr: a #GstRTSPAddress
76 * Free @addr and releasing it back into the pool when owned by a
80 gst_rtsp_address_free (GstRTSPAddress * addr)
82 g_return_if_fail (addr != NULL);
85 /* unrefs the pool and sets it to NULL */
86 gst_rtsp_address_pool_release_address (addr->pool, addr);
88 g_free (addr->address);
89 g_slice_free (GstRTSPAddress, addr);
92 G_DEFINE_BOXED_TYPE (GstRTSPAddress, gst_rtsp_address,
93 (GBoxedCopyFunc) gst_rtsp_address_copy,
94 (GBoxedFreeFunc) gst_rtsp_address_free);
96 GST_DEBUG_CATEGORY_STATIC (rtsp_address_pool_debug);
97 #define GST_CAT_DEFAULT rtsp_address_pool_debug
99 #define GST_RTSP_ADDRESS_POOL_GET_PRIVATE(obj) \
100 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_ADDRESS_POOL, GstRTSPAddressPoolPrivate))
102 struct _GstRTSPAddressPoolPrivate
104 GMutex lock; /* protects everything in this struct */
108 gboolean has_unicast_addresses;
111 #define ADDR_IS_IPV4(a) ((a)->size == 4)
112 #define ADDR_IS_IPV6(a) ((a)->size == 16)
113 #define ADDR_IS_EVEN_PORT(a) (((a)->port & 1) == 0)
129 #define RANGE_IS_SINGLE(r) (memcmp ((r)->min.bytes, (r)->max.bytes, (r)->min.size) == 0)
131 #define gst_rtsp_address_pool_parent_class parent_class
132 G_DEFINE_TYPE (GstRTSPAddressPool, gst_rtsp_address_pool, G_TYPE_OBJECT);
134 static void gst_rtsp_address_pool_finalize (GObject * obj);
137 gst_rtsp_address_pool_class_init (GstRTSPAddressPoolClass * klass)
139 GObjectClass *gobject_class;
141 gobject_class = G_OBJECT_CLASS (klass);
143 gobject_class->finalize = gst_rtsp_address_pool_finalize;
145 g_type_class_add_private (klass, sizeof (GstRTSPAddressPoolPrivate));
147 GST_DEBUG_CATEGORY_INIT (rtsp_address_pool_debug, "rtspaddresspool", 0,
148 "GstRTSPAddressPool");
152 gst_rtsp_address_pool_init (GstRTSPAddressPool * pool)
154 pool->priv = GST_RTSP_ADDRESS_POOL_GET_PRIVATE (pool);
156 g_mutex_init (&pool->priv->lock);
160 free_range (AddrRange * range)
162 g_slice_free (AddrRange, range);
166 gst_rtsp_address_pool_finalize (GObject * obj)
168 GstRTSPAddressPool *pool;
170 pool = GST_RTSP_ADDRESS_POOL (obj);
172 g_list_free_full (pool->priv->addresses, (GDestroyNotify) free_range);
173 g_list_free_full (pool->priv->allocated, (GDestroyNotify) free_range);
174 g_mutex_clear (&pool->priv->lock);
176 G_OBJECT_CLASS (parent_class)->finalize (obj);
180 * gst_rtsp_address_pool_new:
182 * Make a new #GstRTSPAddressPool.
184 * Returns: (transfer full): a new #GstRTSPAddressPool
187 gst_rtsp_address_pool_new (void)
189 GstRTSPAddressPool *pool;
191 pool = g_object_new (GST_TYPE_RTSP_ADDRESS_POOL, NULL);
197 * gst_rtsp_address_pool_clear:
198 * @pool: a #GstRTSPAddressPool
200 * Clear all addresses in @pool. There should be no outstanding
204 gst_rtsp_address_pool_clear (GstRTSPAddressPool * pool)
206 GstRTSPAddressPoolPrivate *priv;
208 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
209 g_return_if_fail (pool->priv->allocated == NULL);
213 g_mutex_lock (&priv->lock);
214 g_list_free_full (priv->addresses, (GDestroyNotify) free_range);
215 priv->addresses = NULL;
216 g_mutex_unlock (&priv->lock);
220 fill_address (const gchar * address, guint16 port, Addr * addr,
221 gboolean is_multicast)
225 inet = g_inet_address_new_from_string (address);
229 if (is_multicast != g_inet_address_get_is_multicast (inet)) {
230 g_object_unref (inet);
234 addr->size = g_inet_address_get_native_size (inet);
235 memcpy (addr->bytes, g_inet_address_to_bytes (inet), addr->size);
236 g_object_unref (inet);
243 get_address_string (Addr * addr)
248 inet = g_inet_address_new_from_bytes (addr->bytes,
249 addr->size == 4 ? G_SOCKET_FAMILY_IPV4 : G_SOCKET_FAMILY_IPV6);
250 res = g_inet_address_to_string (inet);
251 g_object_unref (inet);
257 * gst_rtsp_address_pool_add_range:
258 * @pool: a #GstRTSPAddressPool
259 * @min_address: a minimum address to add
260 * @max_address: a maximum address to add
261 * @min_port: the minimum port
262 * @max_port: the maximum port
263 * @ttl: a TTL or 0 for unicast addresses
265 * Adds the addresses from @min_addess to @max_address (inclusive)
266 * to @pool. The valid port range for the addresses will be from @min_port to
267 * @max_port inclusive.
269 * When @ttl is 0, @min_address and @max_address should be unicast addresses.
270 * @min_address and @max_address can be set to
271 * #GST_RTSP_ADDRESS_POOL_ANY_IPV4 or #GST_RTSP_ADDRESS_POOL_ANY_IPV6 to bind
272 * to all available IPv4 or IPv6 addresses.
274 * When @ttl > 0, @min_address and @max_address should be multicast addresses.
276 * Returns: %TRUE if the addresses could be added.
279 gst_rtsp_address_pool_add_range (GstRTSPAddressPool * pool,
280 const gchar * min_address, const gchar * max_address,
281 guint16 min_port, guint16 max_port, guint8 ttl)
284 GstRTSPAddressPoolPrivate *priv;
285 gboolean is_multicast;
287 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
288 g_return_val_if_fail (min_port <= max_port, FALSE);
292 is_multicast = ttl != 0;
294 range = g_slice_new0 (AddrRange);
296 if (!fill_address (min_address, min_port, &range->min, is_multicast))
298 if (!fill_address (max_address, max_port, &range->max, is_multicast))
301 if (range->min.size != range->max.size)
303 if (memcmp (range->min.bytes, range->max.bytes, range->min.size) > 0)
308 GST_DEBUG_OBJECT (pool, "adding %s-%s:%u-%u ttl %u", min_address, max_address,
309 min_port, max_port, ttl);
311 g_mutex_lock (&priv->lock);
312 priv->addresses = g_list_prepend (priv->addresses, range);
315 priv->has_unicast_addresses = TRUE;
316 g_mutex_unlock (&priv->lock);
323 GST_ERROR_OBJECT (pool, "invalid address range %s-%s", min_address,
325 g_slice_free (AddrRange, range);
331 inc_address (Addr * addr, guint count)
337 for (i = addr->size - 1; i >= 0 && carry > 0; i--) {
338 carry += addr->bytes[i];
339 addr->bytes[i] = carry & 0xff;
344 /* tells us the number of addresses between min_addr and max_addr */
346 diff_address (Addr * max_addr, Addr * min_addr)
351 g_return_val_if_fail (min_addr->size == max_addr->size, 0);
353 for (i = 0; i < min_addr->size; i++) {
354 g_return_val_if_fail (result < (1 << 24), result);
357 result += max_addr->bytes[i] - min_addr->bytes[i];
364 split_range (GstRTSPAddressPool * pool, AddrRange * range, guint skip_addr,
365 guint skip_port, gint n_ports)
367 GstRTSPAddressPoolPrivate *priv = pool->priv;
371 temp = g_slice_dup (AddrRange, range);
372 memcpy (temp->max.bytes, temp->min.bytes, temp->min.size);
373 inc_address (&temp->max, skip_addr - 1);
374 priv->addresses = g_list_prepend (priv->addresses, temp);
376 inc_address (&range->min, skip_addr);
379 if (!RANGE_IS_SINGLE (range)) {
380 /* min and max are not the same, we have more than one address. */
381 temp = g_slice_dup (AddrRange, range);
382 /* increment the range min address */
383 inc_address (&temp->min, 1);
384 /* and store back in pool */
385 priv->addresses = g_list_prepend (priv->addresses, temp);
387 /* adjust range with only the first address */
388 memcpy (range->max.bytes, range->min.bytes, range->min.size);
391 /* range now contains only one single address */
393 /* make a range with the skipped ports */
394 temp = g_slice_dup (AddrRange, range);
395 temp->max.port = temp->min.port + skip_port - 1;
396 /* and store back in pool */
397 priv->addresses = g_list_prepend (priv->addresses, temp);
399 /* increment range port */
400 range->min.port += skip_port;
402 /* range now contains single address with desired port number */
403 if (range->max.port - range->min.port + 1 > n_ports) {
404 /* make a range with the remaining ports */
405 temp = g_slice_dup (AddrRange, range);
406 temp->min.port += n_ports;
407 /* and store back in pool */
408 priv->addresses = g_list_prepend (priv->addresses, temp);
410 /* and truncate port */
411 range->max.port = range->min.port + n_ports - 1;
417 * gst_rtsp_address_pool_acquire_address:
418 * @pool: a #GstRTSPAddressPool
420 * @n_ports: the amount of ports
422 * Take an address and ports from @pool. @flags can be used to control the
423 * allocation. @n_ports consecutive ports will be allocated of which the first
424 * one can be found in @port.
426 * Returns: (nullable): a #GstRTSPAddress that should be freed with
427 * gst_rtsp_address_free after use or %NULL when no address could be
431 gst_rtsp_address_pool_acquire_address (GstRTSPAddressPool * pool,
432 GstRTSPAddressFlags flags, gint n_ports)
434 GstRTSPAddressPoolPrivate *priv;
437 GstRTSPAddress *addr;
439 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), NULL);
440 g_return_val_if_fail (n_ports > 0, NULL);
446 g_mutex_lock (&priv->lock);
447 /* go over available ranges */
448 for (walk = priv->addresses; walk; walk = next) {
455 /* check address type when given */
456 if (flags & GST_RTSP_ADDRESS_FLAG_IPV4 && !ADDR_IS_IPV4 (&range->min))
458 if (flags & GST_RTSP_ADDRESS_FLAG_IPV6 && !ADDR_IS_IPV6 (&range->min))
460 if (flags & GST_RTSP_ADDRESS_FLAG_MULTICAST && range->ttl == 0)
462 if (flags & GST_RTSP_ADDRESS_FLAG_UNICAST && range->ttl != 0)
465 /* check for enough ports */
466 ports = range->max.port - range->min.port + 1;
467 if (flags & GST_RTSP_ADDRESS_FLAG_EVEN_PORT
468 && !ADDR_IS_EVEN_PORT (&range->min))
472 if (ports - skip < n_ports)
475 /* we found a range, remove from the list */
476 priv->addresses = g_list_delete_link (priv->addresses, walk);
477 /* now split and exit our loop */
478 result = split_range (pool, range, 0, skip, n_ports);
479 priv->allocated = g_list_prepend (priv->allocated, result);
482 g_mutex_unlock (&priv->lock);
485 addr = g_slice_new0 (GstRTSPAddress);
486 addr->pool = g_object_ref (pool);
487 addr->address = get_address_string (&result->min);
488 addr->n_ports = n_ports;
489 addr->port = result->min.port;
490 addr->ttl = result->ttl;
493 GST_DEBUG_OBJECT (pool, "got address %s:%u ttl %u", addr->address,
494 addr->port, addr->ttl);
501 * gst_rtsp_address_pool_release_address:
502 * @pool: a #GstRTSPAddressPool
505 * Release a previously acquired address (with
506 * gst_rtsp_address_pool_acquire_address()) back into @pool.
509 gst_rtsp_address_pool_release_address (GstRTSPAddressPool * pool,
510 GstRTSPAddress * addr)
512 GstRTSPAddressPoolPrivate *priv;
516 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
517 g_return_if_fail (addr != NULL);
518 g_return_if_fail (addr->pool == pool);
523 /* we don't want to free twice */
527 g_mutex_lock (&priv->lock);
528 find = g_list_find (priv->allocated, range);
532 priv->allocated = g_list_delete_link (priv->allocated, find);
534 /* FIXME, merge and do something clever */
535 priv->addresses = g_list_prepend (priv->addresses, range);
536 g_mutex_unlock (&priv->lock);
538 g_object_unref (pool);
545 g_warning ("Released unknown address %p", addr);
546 g_mutex_unlock (&priv->lock);
552 dump_range (AddrRange * range, GstRTSPAddressPool * pool)
554 gchar *addr1, *addr2;
556 addr1 = get_address_string (&range->min);
557 addr2 = get_address_string (&range->max);
558 g_print (" address %s-%s, port %u-%u, ttl %u\n", addr1, addr2,
559 range->min.port, range->max.port, range->ttl);
565 * gst_rtsp_address_pool_dump:
566 * @pool: a #GstRTSPAddressPool
568 * Dump the free and allocated addresses to stdout.
571 gst_rtsp_address_pool_dump (GstRTSPAddressPool * pool)
573 GstRTSPAddressPoolPrivate *priv;
575 g_return_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool));
579 g_mutex_lock (&priv->lock);
581 g_list_foreach (priv->addresses, (GFunc) dump_range, pool);
582 g_print ("allocated:\n");
583 g_list_foreach (priv->allocated, (GFunc) dump_range, pool);
584 g_mutex_unlock (&priv->lock);
588 find_address_in_ranges (GList * addresses, Addr * addr, guint port,
589 guint n_ports, guint ttl)
593 /* go over available ranges */
594 for (walk = addresses; walk; walk = next) {
600 /* Not the right type of address */
601 if (range->min.size != addr->size)
604 /* Check that the address is in the interval */
605 if (memcmp (range->min.bytes, addr->bytes, addr->size) > 0 ||
606 memcmp (range->max.bytes, addr->bytes, addr->size) < 0)
609 /* Make sure the requested ports are inside the range */
610 if (port < range->min.port || port + n_ports - 1 > range->max.port)
613 if (ttl != range->ttl)
623 * gst_rtsp_address_pool_reserve_address:
624 * @pool: a #GstRTSPAddressPool
625 * @ip_address: The IP address to reserve
626 * @port: The first port to reserve
627 * @n_ports: The number of ports
628 * @ttl: The requested ttl
629 * @address: (out): storage for a #GstRTSPAddress
631 * Take a specific address and ports from @pool. @n_ports consecutive
632 * ports will be allocated of which the first one can be found in
635 * If @ttl is 0, @address should be a unicast address. If @ttl > 0, @address
636 * should be a valid multicast address.
638 * Returns: #GST_RTSP_ADDRESS_POOL_OK if an address was reserved. The address
639 * is returned in @address and should be freed with gst_rtsp_address_free
642 GstRTSPAddressPoolResult
643 gst_rtsp_address_pool_reserve_address (GstRTSPAddressPool * pool,
644 const gchar * ip_address, guint port, guint n_ports, guint ttl,
645 GstRTSPAddress ** address)
647 GstRTSPAddressPoolPrivate *priv;
650 AddrRange *addr_range;
651 GstRTSPAddress *addr;
652 gboolean is_multicast;
653 GstRTSPAddressPoolResult result;
655 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool),
656 GST_RTSP_ADDRESS_POOL_EINVAL);
657 g_return_val_if_fail (ip_address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
658 g_return_val_if_fail (port > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
659 g_return_val_if_fail (n_ports > 0, GST_RTSP_ADDRESS_POOL_EINVAL);
660 g_return_val_if_fail (address != NULL, GST_RTSP_ADDRESS_POOL_EINVAL);
665 is_multicast = ttl != 0;
667 if (!fill_address (ip_address, port, &input_addr, is_multicast))
670 g_mutex_lock (&priv->lock);
671 list = find_address_in_ranges (priv->addresses, &input_addr, port, n_ports,
674 AddrRange *range = list->data;
675 guint skip_port, skip_addr;
677 skip_addr = diff_address (&input_addr, &range->min);
678 skip_port = port - range->min.port;
680 GST_DEBUG_OBJECT (pool, "diff 0x%08x/%u", skip_addr, skip_port);
682 /* we found a range, remove from the list */
683 priv->addresses = g_list_delete_link (priv->addresses, list);
684 /* now split and exit our loop */
685 addr_range = split_range (pool, range, skip_addr, skip_port, n_ports);
686 priv->allocated = g_list_prepend (priv->allocated, addr_range);
690 addr = g_slice_new0 (GstRTSPAddress);
691 addr->pool = g_object_ref (pool);
692 addr->address = get_address_string (&addr_range->min);
693 addr->n_ports = n_ports;
694 addr->port = addr_range->min.port;
695 addr->ttl = addr_range->ttl;
696 addr->priv = addr_range;
698 result = GST_RTSP_ADDRESS_POOL_OK;
699 GST_DEBUG_OBJECT (pool, "reserved address %s:%u ttl %u", addr->address,
700 addr->port, addr->ttl);
702 /* We failed to reserve the address. Check if it was because the address
703 * was already in use or if it wasn't in the pool to begin with */
704 list = find_address_in_ranges (priv->allocated, &input_addr, port, n_ports,
707 result = GST_RTSP_ADDRESS_POOL_ERESERVED;
709 result = GST_RTSP_ADDRESS_POOL_ERANGE;
712 g_mutex_unlock (&priv->lock);
720 GST_ERROR_OBJECT (pool, "invalid address %s:%u/%u/%u", ip_address,
723 return GST_RTSP_ADDRESS_POOL_EINVAL;
728 * gst_rtsp_address_pool_has_unicast_addresses:
729 * @pool: a #GstRTSPAddressPool
731 * Used to know if the pool includes any unicast addresses.
733 * Returns: %TRUE if the pool includes any unicast addresses, %FALSE otherwise
737 gst_rtsp_address_pool_has_unicast_addresses (GstRTSPAddressPool * pool)
739 GstRTSPAddressPoolPrivate *priv;
740 gboolean has_unicast_addresses;
742 g_return_val_if_fail (GST_IS_RTSP_ADDRESS_POOL (pool), FALSE);
746 g_mutex_lock (&priv->lock);
747 has_unicast_addresses = priv->has_unicast_addresses;
748 g_mutex_unlock (&priv->lock);
750 return has_unicast_addresses;