gst/udp/gstudpnetutils.*: Provide a bunch of helper methods to deal with IPv4 and IPv6 transparently.

Original commit message from CVS:
Patch by: Bruno Santos <brunof at ua dot pt>
* gst/udp/gstudpnetutils.c: (gst_udp_get_addr),
(gst_udp_join_group), (gst_udp_leave_group),
(gst_udp_is_multicast):
* gst/udp/gstudpnetutils.h:
Provide a bunch of helper methods to deal with IPv4 and IPv6
transparently.
* gst/udp/gstmultiudpsink.c: (gst_multiudpsink_class_init),
(gst_multiudpsink_init), (gst_multiudpsink_set_property),
(gst_multiudpsink_get_property), (join_multicast),
(gst_multiudpsink_init_send), (gst_multiudpsink_add_internal),
(gst_multiudpsink_remove):
* gst/udp/gstmultiudpsink.h:
Add multicast TTL and loopback properties.
Use the helper methods to implement ip4 and ip6.
* gst/udp/gstudpsrc.c: (gst_udpsrc_create), (gst_udpsrc_start):
* gst/udp/gstudpsrc.h:
Use the helper methods to implement ip4 and ip6.
Fixes #515962.
This commit is contained in:
Bruno Santos 2008-05-21 09:56:02 +00:00 committed by Wim Taymans
parent 94fb1d9870
commit 570718c2fe
7 changed files with 301 additions and 105 deletions

View file

@ -1,3 +1,28 @@
2008-05-21 Wim Taymans <wim.taymans@collabora.co.uk>
Patch by: Bruno Santos <brunof at ua dot pt>
* gst/udp/gstudpnetutils.c: (gst_udp_get_addr),
(gst_udp_join_group), (gst_udp_leave_group),
(gst_udp_is_multicast):
* gst/udp/gstudpnetutils.h:
Provide a bunch of helper methods to deal with IPv4 and IPv6
transparently.
* gst/udp/gstmultiudpsink.c: (gst_multiudpsink_class_init),
(gst_multiudpsink_init), (gst_multiudpsink_set_property),
(gst_multiudpsink_get_property), (join_multicast),
(gst_multiudpsink_init_send), (gst_multiudpsink_add_internal),
(gst_multiudpsink_remove):
* gst/udp/gstmultiudpsink.h:
Add multicast TTL and loopback properties.
Use the helper methods to implement ip4 and ip6.
* gst/udp/gstudpsrc.c: (gst_udpsrc_create), (gst_udpsrc_start):
* gst/udp/gstudpsrc.h:
Use the helper methods to implement ip4 and ip6.
Fixes #515962.
2008-05-21 Wim Taymans <wim.taymans@collabora.co.uk>
Patch by: Patrick Radizi <patrick dot radizi at axis dot com>

View file

@ -81,6 +81,8 @@ enum
#define DEFAULT_SOCK -1
#define DEFAULT_CLIENTS NULL
#define DEFAULT_AUTO_MULTICAST TRUE
#define DEFAULT_TTL 64
#define DEFAULT_LOOP TRUE
enum
{
@ -91,7 +93,9 @@ enum
PROP_CLOSEFD,
PROP_SOCK,
PROP_CLIENTS,
PROP_AUTO_MULTICAST
PROP_AUTO_MULTICAST,
PROP_TTL,
PROP_LOOP
/* FILL ME */
};
@ -292,6 +296,14 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass)
"Automatically join/leave multicast groups",
"Automatically join/leave the multicast groups, FALSE means user"
" has to do it himself", DEFAULT_AUTO_MULTICAST, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TTL,
g_param_spec_int ("ttl", "Multicast TTL",
"Used for setting the multicast TTL parameter",
0, 255, DEFAULT_TTL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_LOOP,
g_param_spec_boolean ("loop", "Multicast Loopback",
"Used for setting the multicast loop parameter. TRUE = enable,"
" FALSE = disable", DEFAULT_LOOP, G_PARAM_READWRITE));
gstelement_class->change_state = gst_multiudpsink_change_state;
@ -316,6 +328,8 @@ gst_multiudpsink_init (GstMultiUDPSink * sink)
sink->closefd = DEFAULT_CLOSEFD;
sink->externalfd = (sink->sockfd != -1);
sink->auto_multicast = DEFAULT_AUTO_MULTICAST;
sink->ttl = DEFAULT_TTL;
sink->loop = DEFAULT_LOOP;
}
static void
@ -473,6 +487,12 @@ gst_multiudpsink_set_property (GObject * object, guint prop_id,
case PROP_AUTO_MULTICAST:
udpsink->auto_multicast = g_value_get_boolean (value);
break;
case PROP_TTL:
udpsink->ttl = g_value_get_int (value);
break;
case PROP_LOOP:
udpsink->loop = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -510,6 +530,12 @@ gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_AUTO_MULTICAST:
g_value_set_boolean (value, udpsink->auto_multicast);
break;
case PROP_TTL:
g_value_set_int (value, udpsink->ttl);
break;
case PROP_LOOP:
g_value_set_boolean (value, udpsink->loop);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -517,33 +543,13 @@ gst_multiudpsink_get_property (GObject * object, guint prop_id, GValue * value,
}
static void
join_multicast (GstUDPClient * client)
join_multicast (GstUDPClient * client, gboolean loop, int ttl)
{
unsigned char ttl = 64;
unsigned char loop = 1;
/* Joining the multicast group */
/* FIXME, can we use multicast and unicast over the same
* socket? if not, search for socket of this multicast group or
* create a new one. */
if (setsockopt (*(client->sock), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&(client->multi_addr), sizeof (client->multi_addr)) < 0)
perror ("setsockopt IP_ADD_MEMBERSHIP\n");
if (setsockopt (*(client->sock), IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof (ttl)) < 0)
perror ("setsockopt IP_MULTICAST_TTL\n");
if (setsockopt (*(client->sock), IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
sizeof (loop)) < 0)
perror ("setsockopt IP_MULTICAST_LOOP\n");
}
static void
leave_multicast (GstUDPClient * client)
{
if (setsockopt (*(client->sock), IPPROTO_IP, IP_DROP_MEMBERSHIP,
&(client->multi_addr), sizeof (client->multi_addr)) < 0)
GST_WARNING ("setsockopt IP_DROP_MEMBERSHIP failed '%s'",
g_strerror (errno));
gst_udp_join_group (*(client->sock), loop, ttl, &client->theiraddr);
}
/* create a socket for sending to remote machine */
@ -557,8 +563,9 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink)
if (sink->sockfd == -1) {
/* create sender socket */
if ((sink->sock = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
goto no_socket;
if ((sink->sock = socket (AF_INET6, SOCK_DGRAM, 0)) == -1)
if ((sink->sock = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
goto no_socket;
sink->externalfd = FALSE;
} else {
@ -580,8 +587,8 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink)
for (clients = sink->clients; clients; clients = g_list_next (clients)) {
client = (GstUDPClient *) clients->data;
if (client->multi_addr.imr_multiaddr.s_addr && sink->auto_multicast)
join_multicast (client);
if (gst_udp_is_multicast (&client->theiraddr) && sink->auto_multicast)
join_multicast (client, sink->loop, sink->ttl);
}
return TRUE;
@ -612,8 +619,6 @@ static void
gst_multiudpsink_add_internal (GstMultiUDPSink * sink, const gchar * host,
gint port, gboolean lock)
{
struct hostent *he;
struct in_addr addr;
GstUDPClient *client;
GTimeVal now;
@ -623,42 +628,19 @@ gst_multiudpsink_add_internal (GstMultiUDPSink * sink, const gchar * host,
client->port = port;
client->sock = &sink->sock;
memset (&client->theiraddr, 0, sizeof (client->theiraddr));
memset (&client->multi_addr, 0, sizeof (client->multi_addr));
client->theiraddr.sin_family = AF_INET; /* host byte order */
client->theiraddr.sin_port = g_htons (port); /* short, network byte order */
if (gst_udp_get_addr (host, port, &client->theiraddr) < 0)
goto getaddrinfo_error;
g_get_current_time (&now);
client->connect_time = GST_TIMEVAL_TO_TIME (now);
/* if its an IP address */
if (inet_aton (host, &addr)) {
/* check if its a multicast address */
if ((g_ntohl (addr.s_addr) & 0xf0000000) == 0xe0000000) {
GST_DEBUG_OBJECT (sink, "multicast address detected");
client->multi_addr.imr_multiaddr.s_addr = addr.s_addr;
client->multi_addr.imr_interface.s_addr = INADDR_ANY;
client->theiraddr.sin_addr = client->multi_addr.imr_multiaddr;
} else {
GST_DEBUG_OBJECT (sink, "normal address detected");
client->theiraddr.sin_addr = *((struct in_addr *) &addr);
}
/* if init_send has already been called, set sockopts for multicast */
if (*client->sock > 0 && client->multi_addr.imr_multiaddr.s_addr &&
sink->auto_multicast)
join_multicast (client);
}
/* we dont need to lookup for localhost */
else if (strcmp (host, "localhost") == 0 && inet_aton ("127.0.0.1", &addr)) {
client->theiraddr.sin_addr = *((struct in_addr *) &addr);
}
/* if its a hostname */
else if ((he = gethostbyname (host))) {
client->theiraddr.sin_addr = *((struct in_addr *) he->h_addr);
/* check if its a multicast address */
if (*client->sock > 0 && gst_udp_is_multicast (&client->theiraddr) &&
sink->auto_multicast) {
GST_DEBUG_OBJECT (sink, "multicast address detected");
join_multicast (client, sink->loop, sink->ttl);
} else {
goto host_error;
GST_DEBUG_OBJECT (sink, "normal address detected");
}
if (lock)
@ -673,9 +655,9 @@ gst_multiudpsink_add_internal (GstMultiUDPSink * sink, const gchar * host,
return;
/* ERRORS */
host_error:
getaddrinfo_error:
{
GST_WARNING_OBJECT (sink, "hostname lookup error?");
GST_WARNING_OBJECT (sink, "getaddrinfo lookup error?");
g_free (client->host);
g_free (client);
return;
@ -728,9 +710,9 @@ gst_multiudpsink_remove (GstMultiUDPSink * sink, const gchar * host, gint port)
g_get_current_time (&now);
client->disconnect_time = GST_TIMEVAL_TO_TIME (now);
if (*(client->sock) != -1 && client->multi_addr.imr_multiaddr.s_addr
if (*(client->sock) != -1 && gst_udp_is_multicast (&client->theiraddr)
&& sink->auto_multicast)
leave_multicast (client);
gst_udp_leave_group (*(client->sock), &client->theiraddr);
/* Unlock to emit signal before we delete the actual client */
g_mutex_unlock (sink->client_lock);

View file

@ -40,8 +40,7 @@ typedef struct _GstMultiUDPSinkClass GstMultiUDPSinkClass;
typedef struct {
int *sock;
struct sockaddr_in theiraddr;
struct ip_mreq multi_addr;
struct sockaddr_storage theiraddr;
gchar *host;
gint port;
@ -72,6 +71,8 @@ struct _GstMultiUDPSink {
gboolean externalfd;
gboolean auto_multicast;
gint ttl;
gboolean loop;
};
struct _GstMultiUDPSinkClass {

View file

@ -22,21 +22,14 @@
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <memory.h>
#include "gstudpnetutils.h"
#ifdef G_OS_WIN32
int
gst_udp_net_utils_win32_inet_aton (const char *c, struct in_addr *paddr)
{
paddr->s_addr = inet_addr (c);
if (paddr->s_addr == INADDR_NONE)
return 0;
return 1;
}
gboolean
gst_udp_net_utils_win32_wsa_startup (GstObject * obj)
{
@ -60,3 +53,178 @@ gst_udp_net_utils_win32_wsa_startup (GstObject * obj)
}
#endif
int
gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr)
{
struct addrinfo hints, *res, *nres;
char service[NI_MAXSERV];
int ret;
memset (&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
snprintf (service, sizeof (service) - 1, "%d", port);
service[sizeof (service) - 1] = '\0';
if ((ret = getaddrinfo (hostname, (port == -1) ? NULL : service, &hints,
&res)) < 0) {
return ret;
}
nres = res;
while (nres) {
if (nres->ai_family == AF_INET || nres->ai_family == AF_INET6)
break;
nres = nres->ai_next;
}
if (nres) {
memcpy (addr, nres->ai_addr, nres->ai_addrlen);
} else {
errno = EAI_ADDRFAMILY;
ret = -1;
}
freeaddrinfo (res);
return ret;
}
int
gst_udp_join_group (int sockfd, gboolean loop, int ttl,
struct sockaddr_storage *addr)
{
int ret = -1;
int l = (loop == FALSE) ? 0 : 1;
switch (addr->ss_family) {
case AF_INET:
{
struct ip_mreq mreq4;
mreq4.imr_multiaddr.s_addr =
((struct sockaddr_in *) addr)->sin_addr.s_addr;
mreq4.imr_interface.s_addr = INADDR_ANY;
if ((ret =
setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &l,
sizeof (l))) < 0)
return ret;
if ((ret =
setsockopt (sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof (ttl))) < 0)
return ret;
if ((ret =
setsockopt (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *) &mreq4, sizeof (mreq4))) < 0)
return ret;
}
break;
case AF_INET6:
{
struct ipv6_mreq mreq6;
memcpy (&mreq6.ipv6mr_multiaddr,
&(((struct sockaddr_in6 *) addr)->sin6_addr),
sizeof (struct in6_addr));
mreq6.ipv6mr_interface = 0;
if ((ret =
setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &l,
sizeof (l))) < 0)
return ret;
if ((ret =
setsockopt (sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl,
sizeof (ttl))) < 0)
return ret;
if ((ret =
setsockopt (sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
(const void *) &mreq6, sizeof (mreq6))) < 0)
return ret;
}
break;
default:
errno = EAFNOSUPPORT;
}
return ret;
}
int
gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr)
{
int ret = -1;
switch (addr->ss_family) {
case AF_INET:
{
struct ip_mreq mreq4;
mreq4.imr_multiaddr.s_addr =
((struct sockaddr_in *) addr)->sin_addr.s_addr;
mreq4.imr_interface.s_addr = INADDR_ANY;
if ((ret =
setsockopt (sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(const void *) &mreq4, sizeof (mreq4))) < 0)
return ret;
}
break;
case AF_INET6:
{
struct ipv6_mreq mreq6;
memcpy (&mreq6.ipv6mr_multiaddr,
&(((struct sockaddr_in6 *) addr)->sin6_addr),
sizeof (struct in6_addr));
mreq6.ipv6mr_interface = 0;
if ((ret =
setsockopt (sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP,
(const void *) &mreq6, sizeof (mreq6))) < 0)
return ret;
}
break;
default:
errno = EAFNOSUPPORT;
}
return ret;
}
int
gst_udp_is_multicast (struct sockaddr_storage *addr)
{
int ret = -1;
switch (addr->ss_family) {
case AF_INET:
{
struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
ret = IN_MULTICAST (ntohl (addr4->sin_addr.s_addr));
}
break;
case AF_INET6:
{
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
ret = IN6_IS_ADDR_MULTICAST (&addr6->sin6_addr);
}
break;
default:
errno = EAFNOSUPPORT;
}
return ret;
}

View file

@ -52,7 +52,6 @@
#define IOCTL_SOCKET ioctlsocket
#define CLOSE_SOCKET(sock) closesocket(sock)
#define setsockopt(sock,l,opt,val,len) setsockopt(sock,l,opt,(char *)(val),len)
#define inet_aton(c,addr) gst_udp_net_utils_win32_inet_aton ((c),(addr))
#define WSA_STARTUP(obj) gst_udp_net_utils_win32_wsa_startup(GST_OBJECT(obj))
#define WSA_CLEANUP(obj) WSACleanup ()
@ -68,10 +67,15 @@
#ifdef G_OS_WIN32
int gst_udp_net_utils_win32_inet_aton (const char *c, struct in_addr * addr);
gboolean gst_udp_net_utils_win32_wsa_startup (GstObject * obj);
#endif
int gst_udp_get_addr (const char *hostname, int port, struct sockaddr_storage *addr);
int gst_udp_is_multicast (struct sockaddr_storage *addr);
int gst_udp_join_group (int sockfd, gboolean loop, int ttl, struct sockaddr_storage *addr);
int gst_udp_leave_group (int sockfd, struct sockaddr_storage *addr);
#endif /* __GST_UDP_NET_UTILS_H__*/

View file

@ -349,7 +349,7 @@ gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
{
GstUDPSrc *udpsrc;
GstNetBuffer *outbuf;
struct sockaddr_in tmpaddr;
struct sockaddr_storage tmpaddr;
socklen_t len;
guint8 *pktdata;
gint pktsize;
@ -468,8 +468,28 @@ no_select:
GST_BUFFER_DATA (outbuf) = pktdata;
GST_BUFFER_SIZE (outbuf) = ret;
gst_netaddress_set_ip4_address (&outbuf->from, tmpaddr.sin_addr.s_addr,
tmpaddr.sin_port);
switch (tmpaddr.ss_family) {
case AF_INET:
{
gst_netaddress_set_ip4_address (&outbuf->from,
((struct sockaddr_in *) &tmpaddr)->sin_addr.s_addr,
((struct sockaddr_in *) &tmpaddr)->sin_port);
}
break;
case AF_INET6:
{
guint8 ip6[16];
memcpy (ip6, &((struct sockaddr_in6 *) &tmpaddr)->sin6_addr,
sizeof (ip6));
gst_netaddress_set_ip6_address (&outbuf->from, ip6,
((struct sockaddr_in *) &tmpaddr)->sin_port);
}
break;
default:
errno = EAFNOSUPPORT;
goto receive_error;
}
gst_buffer_set_caps (GST_BUFFER_CAST (outbuf), udpsrc->caps);
@ -681,7 +701,7 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
{
guint bc_val;
gint reuse;
struct sockaddr_in my_addr;
struct sockaddr_storage my_addr;
guint len;
int port;
GstUDPSrc *src;
@ -690,12 +710,12 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
src = GST_UDPSRC (bsrc);
if (!inet_aton (src->multi_group, &(src->multi_addr.imr_multiaddr)))
src->multi_addr.imr_multiaddr.s_addr = 0;
if (src->sockfd == -1) {
/* need to allocate a socket */
if ((ret = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
if ((ret =
gst_udp_get_addr (src->multi_group, src->port, &src->myaddr)) < 0)
goto getaddrinfo_error;
if ((ret = socket (src->myaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
goto no_socket;
src->sock.fd = ret;
@ -707,15 +727,6 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
sizeof (reuse))) < 0)
goto setsockopt_error;
memset (&src->myaddr, 0, sizeof (src->myaddr));
src->myaddr.sin_family = AF_INET; /* host byte order */
src->myaddr.sin_port = g_htons (src->port); /* short, network byte order */
if (src->multi_addr.imr_multiaddr.s_addr)
src->myaddr.sin_addr.s_addr = src->multi_addr.imr_multiaddr.s_addr;
else
src->myaddr.sin_addr.s_addr = INADDR_ANY;
GST_DEBUG_OBJECT (src, "binding on port %d", src->port);
if ((ret = bind (src->sock.fd, (struct sockaddr *) &src->myaddr,
sizeof (src->myaddr))) < 0)
@ -726,11 +737,9 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
src->externalfd = TRUE;
}
if (src->multi_addr.imr_multiaddr.s_addr) {
src->multi_addr.imr_interface.s_addr = INADDR_ANY;
if ((ret =
setsockopt (src->sock.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&src->multi_addr, sizeof (src->multi_addr))) < 0)
if (gst_udp_is_multicast (&src->myaddr)) {
ret = gst_udp_join_group (src->sock.fd, TRUE, src->ttl, &src->myaddr);
if (ret < 0)
goto membership;
}
@ -769,7 +778,9 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
sizeof (bc_val))) < 0)
goto no_broadcast;
port = g_ntohs (my_addr.sin_port);
/* NOTE: sockaddr_in.sin_port works for ipv4 and ipv6 because sin_port
* follows ss_family on both */
port = ntohs (((struct sockaddr_in *) &my_addr)->sin_port);
GST_DEBUG_OBJECT (src, "bound, on port %d", port);
if (port != src->port) {
src->port = port;
@ -777,7 +788,7 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
g_object_notify (G_OBJECT (src), "port");
}
src->myaddr.sin_port = g_htons (src->port + 1);
((struct sockaddr_in *) &src->myaddr)->sin_port = htons (src->port + 1);
if ((src->fdset = gst_poll_new (TRUE)) == NULL)
goto no_fdset;
@ -788,6 +799,12 @@ gst_udpsrc_start (GstBaseSrc * bsrc)
return TRUE;
/* ERRORS */
getaddrinfo_error:
{
GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL),
("getaddrinfo failed %d: %s (%d)", ret, g_strerror (errno), errno));
return FALSE;
}
no_socket:
{
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),

View file

@ -68,8 +68,7 @@ struct _GstUDPSrc {
GstPoll *fdset;
gboolean externalfd;
struct sockaddr_in myaddr;
struct ip_mreq multi_addr;
struct sockaddr_storage myaddr;
};
struct _GstUDPSrcClass {