#include "config.h"
#endif
+/* Needed to get struct in6_pktinfo */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
#include <string.h>
#include "gstudpsrc.h"
#include <gio/gnetworking.h>
+/* Control messages for getting the destination address */
+#ifdef IP_PKTINFO
+GType gst_ip_pktinfo_message_get_type (void);
+
+#define GST_TYPE_IP_PKTINFO_MESSAGE (gst_ip_pktinfo_message_get_type ())
+#define GST_IP_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessage))
+#define GST_IP_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessageClass))
+#define GST_IS_IP_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IP_PKTINFO_MESSAGE))
+#define GST_IS_IP_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IP_PKTINFO_MESSAGE))
+#define GST_IP_PKTINFO_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessageClass))
+
+typedef struct _GstIPPktinfoMessage GstIPPktinfoMessage;
+typedef struct _GstIPPktinfoMessageClass GstIPPktinfoMessageClass;
+
+struct _GstIPPktinfoMessageClass
+{
+ GSocketControlMessageClass parent_class;
+
+};
+
+struct _GstIPPktinfoMessage
+{
+ GSocketControlMessage parent;
+
+ guint ifindex;
+ struct in_addr spec_dst, addr;
+};
+
+G_DEFINE_TYPE (GstIPPktinfoMessage, gst_ip_pktinfo_message,
+ G_TYPE_SOCKET_CONTROL_MESSAGE);
+
+static gsize
+gst_ip_pktinfo_message_get_size (GSocketControlMessage * message)
+{
+ return sizeof (struct in_pktinfo);
+}
+
+static int
+gst_ip_pktinfo_message_get_level (GSocketControlMessage * message)
+{
+ return IPPROTO_IP;
+}
+
+static int
+gst_ip_pktinfo_message_get_msg_type (GSocketControlMessage * message)
+{
+ return IP_PKTINFO;
+}
+
+static GSocketControlMessage *
+gst_ip_pktinfo_message_deserialize (gint level,
+ gint type, gsize size, gpointer data)
+{
+ struct in_pktinfo *pktinfo;
+ GstIPPktinfoMessage *message;
+
+ if (level != IPPROTO_IP || type != IP_PKTINFO)
+ return NULL;
+
+ if (size < sizeof (struct in_pktinfo))
+ return NULL;
+
+ pktinfo = data;
+
+ message = g_object_new (GST_TYPE_IP_PKTINFO_MESSAGE, NULL);
+ message->ifindex = pktinfo->ipi_ifindex;
+ message->spec_dst = pktinfo->ipi_spec_dst;
+ message->addr = pktinfo->ipi_addr;
+
+ return G_SOCKET_CONTROL_MESSAGE (message);
+}
+
+static void
+gst_ip_pktinfo_message_init (GstIPPktinfoMessage * message)
+{
+}
+
+static void
+gst_ip_pktinfo_message_class_init (GstIPPktinfoMessageClass * class)
+{
+ GSocketControlMessageClass *scm_class;
+
+ scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
+ scm_class->get_size = gst_ip_pktinfo_message_get_size;
+ scm_class->get_level = gst_ip_pktinfo_message_get_level;
+ scm_class->get_type = gst_ip_pktinfo_message_get_msg_type;
+ scm_class->deserialize = gst_ip_pktinfo_message_deserialize;
+}
+#endif
+
+#ifdef IPV6_PKTINFO
+GType gst_ipv6_pktinfo_message_get_type (void);
+
+#define GST_TYPE_IPV6_PKTINFO_MESSAGE (gst_ipv6_pktinfo_message_get_type ())
+#define GST_IPV6_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessage))
+#define GST_IPV6_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessageClass))
+#define GST_IS_IPV6_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE))
+#define GST_IS_IPV6_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IPV6_PKTINFO_MESSAGE))
+#define GST_IPV6_PKTINFO_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessageClass))
+
+typedef struct _GstIPV6PktinfoMessage GstIPV6PktinfoMessage;
+typedef struct _GstIPV6PktinfoMessageClass GstIPV6PktinfoMessageClass;
+
+struct _GstIPV6PktinfoMessageClass
+{
+ GSocketControlMessageClass parent_class;
+
+};
+
+struct _GstIPV6PktinfoMessage
+{
+ GSocketControlMessage parent;
+
+ guint ifindex;
+ struct in6_addr addr;
+};
+
+G_DEFINE_TYPE (GstIPV6PktinfoMessage, gst_ipv6_pktinfo_message,
+ G_TYPE_SOCKET_CONTROL_MESSAGE);
+
+static gsize
+gst_ipv6_pktinfo_message_get_size (GSocketControlMessage * message)
+{
+ return sizeof (struct in6_pktinfo);
+}
+
+static int
+gst_ipv6_pktinfo_message_get_level (GSocketControlMessage * message)
+{
+ return IPPROTO_IPV6;
+}
+
+static int
+gst_ipv6_pktinfo_message_get_msg_type (GSocketControlMessage * message)
+{
+ return IPV6_PKTINFO;
+}
+
+static GSocketControlMessage *
+gst_ipv6_pktinfo_message_deserialize (gint level,
+ gint type, gsize size, gpointer data)
+{
+ struct in6_pktinfo *pktinfo;
+ GstIPV6PktinfoMessage *message;
+
+ if (level != IPPROTO_IPV6 || type != IPV6_PKTINFO)
+ return NULL;
+
+ if (size < sizeof (struct in_pktinfo))
+ return NULL;
+
+ pktinfo = data;
+
+ message = g_object_new (GST_TYPE_IPV6_PKTINFO_MESSAGE, NULL);
+ message->ifindex = pktinfo->ipi6_ifindex;
+ message->addr = pktinfo->ipi6_addr;
+
+ return G_SOCKET_CONTROL_MESSAGE (message);
+}
+
+static void
+gst_ipv6_pktinfo_message_init (GstIPV6PktinfoMessage * message)
+{
+}
+
+static void
+gst_ipv6_pktinfo_message_class_init (GstIPV6PktinfoMessageClass * class)
+{
+ GSocketControlMessageClass *scm_class;
+
+ scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
+ scm_class->get_size = gst_ipv6_pktinfo_message_get_size;
+ scm_class->get_level = gst_ipv6_pktinfo_message_get_level;
+ scm_class->get_type = gst_ipv6_pktinfo_message_get_msg_type;
+ scm_class->deserialize = gst_ipv6_pktinfo_message_deserialize;
+}
+
+#endif
+
+#ifdef IP_RECVDSTADDR
+GType gst_ip_recvdstaddr_message_get_type (void);
+
+#define GST_TYPE_IP_RECVDSTADDR_MESSAGE (gst_ip_recvdstaddr_message_get_type ())
+#define GST_IP_RECVDSTADDR_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessage))
+#define GST_IP_RECVDSTADDR_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessageClass))
+#define GST_IS_IP_RECVDSTADDR_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE))
+#define GST_IS_IP_RECVDSTADDR_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IP_RECVDSTADDR_MESSAGE))
+#define GST_IP_RECVDSTADDR_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessageClass))
+
+typedef struct _GstIPRecvdstaddrMessage GstIPRecvdstaddrMessage;
+typedef struct _GstIPRecvdstaddrMessageClass GstIPRecvdstaddrMessageClass;
+
+struct _GstIPRecvdstaddrMessageClass
+{
+ GSocketControlMessageClass parent_class;
+
+};
+
+struct _GstIPRecvdstaddrMessage
+{
+ GSocketControlMessage parent;
+
+ guint ifindex;
+ struct in_addr addr;
+};
+
+G_DEFINE_TYPE (GstIPRecvdstaddrMessage, gst_ip_recvdstaddr_message,
+ G_TYPE_SOCKET_CONTROL_MESSAGE);
+
+static gsize
+gst_ip_recvdstaddr_message_get_size (GSocketControlMessage * message)
+{
+ return sizeof (struct in_addr);
+}
+
+static int
+gst_ip_recvdstaddr_message_get_level (GSocketControlMessage * message)
+{
+ return IPPROTO_IP;
+}
+
+static int
+gst_ip_recvdstaddr_message_get_msg_type (GSocketControlMessage * message)
+{
+ return IP_RECVDSTADDR;
+}
+
+static GSocketControlMessage *
+gst_ip_recvdstaddr_message_deserialize (gint level,
+ gint type, gsize size, gpointer data)
+{
+ struct in_addr *addr;
+ GstIPRecvdstaddrMessage *message;
+
+ if (level != IPPROTO_IP || type != IP_RECVDSTADDR)
+ return NULL;
+
+ if (size < sizeof (struct in_addr))
+ return NULL;
+
+ addr = data;
+
+ message = g_object_new (GST_TYPE_IP_RECVDSTADDR_MESSAGE, NULL);
+ message->addr = g_inet_address_new_from_bytes ((guint8 *) addr, AF_INET);
+
+ return G_SOCKET_CONTROL_MESSAGE (message);
+}
+
+static void
+gst_ip_recvdstaddr_message_init (GstIPRecvdstaddrMessage * message)
+{
+}
+
+static void
+gst_ip_recvdstaddr_message_class_init (GstIPRecvdstaddrMessageClass * class)
+{
+ GSocketControlMessageClass *scm_class;
+
+ scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
+ scm_class->get_size = gst_ip_recvdstaddr_message_get_size;
+ scm_class->get_level = gst_ip_recvdstaddr_message_get_level;
+ scm_class->get_type = gst_ip_recvdstaddr_message_get_msg_type;
+ scm_class->deserialize = gst_ip_recvdstaddr_message_deserialize;
+}
+#endif
+
/* not 100% correct, but a good upper bound for memory allocation purposes */
#define MAX_IPV4_UDP_PACKET_SIZE (65536 - 8)
GST_DEBUG_CATEGORY_INIT (udpsrc_debug, "udpsrc", 0, "UDP src");
+#ifdef IP_PKTINFO
+ GST_TYPE_IP_PKTINFO_MESSAGE;
+#endif
+#ifdef IPV6_PKTINFO
+ GST_TYPE_IPV6_PKTINFO_MESSAGE;
+#endif
+#ifdef IP_RECVDSTADDR
+ GST_TYPE_IP_RECVDSTADDR_MESSAGE;
+#endif
+
gobject_class->set_property = gst_udpsrc_set_property;
gobject_class->get_property = gst_udpsrc_get_property;
gobject_class->finalize = gst_udpsrc_finalize;
GError *err = NULL;
gssize res;
gsize offset;
+ GSocketControlMessage **msgs = NULL;
+ gint n_msgs = 0, i;
udpsrc = GST_UDPSRC_CAST (psrc);
res =
g_socket_receive_message (udpsrc->used_socket, p_saddr, udpsrc->vec, 2,
- NULL, NULL, &flags, udpsrc->cancellable, &err);
+ &msgs, &n_msgs, &flags, udpsrc->cancellable, &err);
if (G_UNLIKELY (res < 0)) {
/* G_IO_ERROR_HOST_UNREACHABLE for a UDP socket means that a packet sent
if (res > udpsrc->max_size)
udpsrc->max_size = res;
+ /* Retry if multicast and the destination address is not ours. We don't want
+ * to receive arbitrary packets */
+ {
+ GInetAddress *iaddr = g_inet_socket_address_get_address (udpsrc->addr);
+ gboolean skip_packet = FALSE;
+ gsize iaddr_size = g_inet_address_get_native_size (iaddr);
+ const guint8 *iaddr_bytes = g_inet_address_to_bytes (iaddr);
+
+ if (g_inet_address_get_is_multicast (iaddr)) {
+
+ for (i = 0; i < n_msgs && !skip_packet; i++) {
+#ifdef IP_PKTINFO
+ if (GST_IS_IP_PKTINFO_MESSAGE (msgs[i])) {
+ GstIPPktinfoMessage *msg = GST_IP_PKTINFO_MESSAGE (msgs[i]);
+
+ if (sizeof (msg->addr) == iaddr_size
+ && memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
+ skip_packet = TRUE;
+ }
+#endif
+#ifdef IPV6_PKTINFO
+ if (GST_IS_IPV6_PKTINFO_MESSAGE (msgs[i])) {
+ GstIPV6PktinfoMessage *msg = GST_IPV6_PKTINFO_MESSAGE (msgs[i]);
+
+ if (sizeof (msg->addr) == iaddr_size
+ && memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
+ skip_packet = TRUE;
+ }
+#endif
+#ifdef IP_RECVDSTADDR
+ if (GST_IS_IP_RECVDSTADDR_MESSAGE (msgs[i])) {
+ GstIPRecvdstaddrMessage *msg = GST_IP_RECVDSTADDR_MESSAGE (msgs[i]);
+
+ if (sizeof (msg->addr) == iaddr_size
+ && memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
+ skip_packet = TRUE;
+ }
+#endif
+ }
+
+ }
+
+ for (i = 0; i < n_msgs; i++) {
+ g_object_unref (msgs[i]);
+ }
+ g_free (msgs);
+
+ if (skip_packet) {
+ GST_DEBUG_OBJECT (udpsrc,
+ "Dropping packet for a different multicast address");
+
+ if (saddr != NULL) {
+ g_object_unref (saddr);
+ saddr = NULL;
+ }
+
+ goto retry;
+ }
+ }
+
outbuf = gst_buffer_new ();
/* append first memory chunk to buffer */
g_inet_socket_address_get_address (src->addr),
FALSE, src->multi_iface, &err))
goto membership;
+
+ if (g_inet_address_get_family (g_inet_socket_address_get_address
+ (src->addr)) == G_SOCKET_FAMILY_IPV4) {
+#if defined(IP_PKTINFO)
+ if (!g_socket_set_option (src->used_socket, IPPROTO_IP, IP_PKTINFO, TRUE,
+ &err)) {
+ GST_WARNING_OBJECT (src, "Failed to enable IP_PKTINFO: %s",
+ err->message);
+ g_clear_error (&err);
+ }
+#elif defined(IP_RECVDSTADDR)
+ if (!g_socket_set_option (src->used_socket, IPPROTO_IP, IP_RECVDSTADDR,
+ TRUE, &err)) {
+ GST_WARNING_OBJECT (src, "Failed to enable IP_RECVDSTADDR: %s",
+ err->message);
+ g_clear_error (&err);
+ }
+#else
+#pragma message("No API available for getting IPv4 destination address")
+ GST_WARNING_OBJECT (src, "No API available for getting IPv4 destination "
+ "address, will receive packets for every destination to our port");
+#endif
+ } else
+ if (g_inet_address_get_family (g_inet_socket_address_get_address
+ (src->addr)) == G_SOCKET_FAMILY_IPV6) {
+#ifdef IPV6_PKTINFO
+#ifdef IPV6_RECVPKTINFO
+ if (!g_socket_set_option (src->used_socket, IPPROTO_IPV6,
+ IPV6_RECVPKTINFO, TRUE, &err)) {
+#else
+ if (!g_socket_set_option (src->used_socket, IPPROTO_IPV6, IPV6_PKTINFO,
+ TRUE, &err)) {
+#endif
+ GST_WARNING_OBJECT (src, "Failed to enable IPV6_PKTINFO: %s",
+ err->message);
+ g_clear_error (&err);
+ }
+#else
+#pragma message("No API available for getting IPv6 destination address")
+ GST_WARNING_OBJECT (src, "No API available for getting IPv6 destination "
+ "address, will receive packets for every destination to our port");
+#endif
+ }
}
/* NOTE: sockaddr_in.sin_port works for ipv4 and ipv6 because sin_port