From: Jens Georg Date: Sun, 2 May 2010 14:41:14 +0000 (+0200) Subject: Port GSSDP to GIO/GSocket X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2cc4c32a92aa8fed337de672bb4479003f214290;p=profile%2Fivi%2FGSSDP.git Port GSSDP to GIO/GSocket --- diff --git a/configure.ac b/configure.ac index 2309b68..4691e76 100644 --- a/configure.ac +++ b/configure.ac @@ -12,8 +12,9 @@ AC_STDC_HEADERS AC_PROG_LIBTOOL AC_CONFIG_MACRO_DIR([m4]) -PKG_CHECK_MODULES(LIBGSSDP, glib-2.0 >= 2.18 \ - gobject-2.0 >= 2.18 \ +PKG_CHECK_MODULES(LIBGSSDP, glib-2.0 >= 2.22 \ + gobject-2.0 >= 2.22 \ + gio-2.0 >= 2.22 \ libsoup-2.4) LIBGTK_REQUIRED=2.12.0 diff --git a/libgssdp/Makefile.am b/libgssdp/Makefile.am index b1d4768..aad8362 100644 --- a/libgssdp/Makefile.am +++ b/libgssdp/Makefile.am @@ -42,6 +42,8 @@ libgssdp_1_0_la_SOURCES = gssdp-client.c \ gssdp-resource-group.c \ gssdp-socket-source.c \ gssdp-socket-source.h \ + gssdp-socket-functions.c \ + gssdp-socket-functions.h \ $(BUILT_SOURCES) libgssdp_1_0_la_LIBADD = $(LIBGSSDP_LIBS) diff --git a/libgssdp/gssdp-client.c b/libgssdp/gssdp-client.c index 3cef6e4..902a97f 100644 --- a/libgssdp/gssdp-client.c +++ b/libgssdp/gssdp-client.c @@ -98,9 +98,13 @@ gssdp_client_set_main_context (GSSDPClient *client, static char * make_server_id (void); static gboolean -request_socket_source_cb (gpointer user_data); +request_socket_source_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data); static gboolean -multicast_socket_source_cb (gpointer user_data); +multicast_socket_source_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data); static gboolean init_network_info (GSSDPClient *client); @@ -122,6 +126,7 @@ static void gssdp_client_constructed (GObject *object) { GSSDPClient *client = GSSDP_CLIENT (object); + GError *error = NULL; /* Make sure all network info is available to us */ if (!init_network_info (client)) @@ -130,11 +135,12 @@ gssdp_client_constructed (GObject *object) /* Set up sockets (Will set errno if it failed) */ client->priv->request_socket = gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_REQUEST, - gssdp_client_get_host_ip (client)); + gssdp_client_get_host_ip (client), + &error); if (client->priv->request_socket != NULL) { g_source_set_callback - ((GSource *) client->priv->request_socket, - request_socket_source_cb, + (client->priv->request_socket->source, + (GSourceFunc) request_socket_source_cb, client, NULL); } else { @@ -143,33 +149,31 @@ gssdp_client_constructed (GObject *object) client->priv->multicast_socket = gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_MULTICAST, - gssdp_client_get_host_ip (client)); + gssdp_client_get_host_ip (client), + &error); if (client->priv->multicast_socket != NULL) { g_source_set_callback - ((GSource *) client->priv->multicast_socket, - multicast_socket_source_cb, + (client->priv->multicast_socket->source, + (GSourceFunc) multicast_socket_source_cb, client, NULL); } errors: if (!client->priv->request_socket || !client->priv->multicast_socket) { - if (client->priv->error) - g_set_error_literal (client->priv->error, - GSSDP_ERROR, - GSSDP_ERROR_FAILED, - g_strerror (errno)); - + if (client->priv->error) { + g_propagate_error (client->priv->error, error); + } return; } - g_source_attach ((GSource *) client->priv->request_socket, + g_source_attach (client->priv->request_socket->source, client->priv->main_context); - g_source_unref ((GSource *) client->priv->request_socket); + g_source_unref (client->priv->request_socket->source); - g_source_attach ((GSource *) client->priv->multicast_socket, + g_source_attach (client->priv->multicast_socket->source, client->priv->main_context); - g_source_unref ((GSource *) client->priv->multicast_socket); + g_source_unref (client->priv->multicast_socket->source); } static void @@ -254,12 +258,12 @@ gssdp_client_dispose (GObject *object) /* Destroy the SocketSources */ if (client->priv->request_socket) { - g_source_destroy ((GSource *) client->priv->request_socket); + gssdp_socket_source_destroy (client->priv->request_socket); client->priv->request_socket = NULL; } if (client->priv->multicast_socket) { - g_source_destroy ((GSource *) client->priv->multicast_socket); + gssdp_socket_source_destroy (client->priv->multicast_socket); client->priv->multicast_socket = NULL; } @@ -583,8 +587,11 @@ _gssdp_client_send_message (GSSDPClient *client, gushort dest_port, const char *message) { - struct sockaddr_in addr; - int socket_fd, res; + gssize res; + GError *error = NULL; + GSocket *socket = NULL; + GInetAddress *inet_address = NULL; + GSocketAddress *address = NULL; g_return_if_fail (GSSDP_IS_CLIENT (client)); g_return_if_fail (message != NULL); @@ -601,25 +608,24 @@ _gssdp_client_send_message (GSSDPClient *client, if (dest_port == 0) dest_port = SSDP_PORT; - socket_fd = gssdp_socket_source_get_fd (client->priv->request_socket); - - memset (&addr, 0, sizeof (addr)); - - addr.sin_family = AF_INET; - addr.sin_port = htons (dest_port); - addr.sin_addr.s_addr = inet_addr (dest_ip); - - res = sendto (socket_fd, - message, - strlen (message), - 0, - (struct sockaddr *) &addr, - sizeof (addr)); + inet_address = g_inet_address_new_from_string (dest_ip); + address = g_inet_socket_address_new (inet_address, dest_port); + socket = gssdp_socket_source_get_socket (client->priv->request_socket); + res = g_socket_send_to (socket, + address, + message, + strlen (message), + NULL, + &error); if (res == -1) { - g_warning ("sendto: Error %d sending message: %s", - errno, strerror (errno)); + g_warning ("g_socket_sendto: Error sending message %s: %s", + message, error->message); + g_error_free (error); } + + g_object_unref (address); + g_object_unref (inet_address); } /** @@ -710,34 +716,35 @@ parse_http_response (char *buf, * Called when data can be read from the socket **/ static gboolean -socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client) +socket_source_cb (GSSDPSocketSource *socket_source, GSSDPClient *client) { - int fd, type, len; - ssize_t bytes; + int type, len; char buf[BUF_SIZE], *end; - struct sockaddr_in addr; - socklen_t addr_size; SoupMessageHeaders *headers; - struct in_addr our_addr; - in_addr_t our_network; + GSocket *socket; + GSocketAddress *address = NULL; + gssize bytes; + GInetAddress *inetaddr; + char *ip_string; + guint16 port; + GError *error = NULL; in_addr_t recv_network; + in_addr_t our_network; + struct in_addr our_addr; + struct sockaddr_in addr; - /* Get FD */ - fd = gssdp_socket_source_get_fd (socket); - - /* Read data */ - addr_size = sizeof (addr); - - bytes = recvfrom (fd, - buf, - BUF_SIZE - 1, /* Leave space for trailing \0 */ - MSG_TRUNC, - (struct sockaddr *) &addr, - &addr_size); + /* Get Socket */ + socket = gssdp_socket_source_get_socket (socket_source); + bytes = g_socket_receive_from (socket, + &address, + buf, + BUF_SIZE - 1, + NULL, + &error); if (bytes == -1) { - g_warning ("Failed to read from socket: %d (%s)", - errno, - strerror (errno)); + g_warning ("Failed to receive from socket: %s", + error->message); + g_error_free (error); return TRUE; } @@ -748,6 +755,18 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client) * on this socket from a particular interface but AFAIK that is not * possible, at least not in a portable way. */ + + if (!g_socket_address_to_native (address, + &addr, + sizeof (struct sockaddr_in), + &error)) { + g_warning ("Could not convert address to native: %s", + error->message); + g_error_free (error); + + return TRUE; + } + recv_network = inet_netof (addr.sin_addr); our_addr.s_addr = inet_addr (gssdp_client_get_host_ip (client)); our_network = inet_netof (our_addr); @@ -755,6 +774,7 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client) return TRUE; if (bytes >= BUF_SIZE) { + g_object_unref (address); g_warning ("Received packet of %u bytes, but the maximum " "buffer size is %d. Packed dropped.", (unsigned int) bytes, BUF_SIZE); @@ -768,6 +788,7 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client) /* Find length */ end = strstr (buf, "\r\n\r\n"); if (!end) { + g_object_unref (address); g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. " "Packed dropped."); @@ -793,24 +814,36 @@ socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client) } /* Emit signal if parsing succeeded */ + inetaddr = g_inet_socket_address_get_address ( + G_INET_SOCKET_ADDRESS (address)); + ip_string = g_inet_address_to_string (inetaddr); + port = g_inet_socket_address_get_port ( + G_INET_SOCKET_ADDRESS (address)); if (type >= 0) { g_signal_emit (client, signals[MESSAGE_RECEIVED], 0, - inet_ntoa (addr.sin_addr), - ntohs (addr.sin_port), + ip_string, + port, type, headers); } + if (ip_string) + g_free (ip_string); + if (headers) soup_message_headers_free (headers); + g_object_unref (address); + return TRUE; } static gboolean -request_socket_source_cb (gpointer user_data) +request_socket_source_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data) { GSSDPClient *client; @@ -820,7 +853,9 @@ request_socket_source_cb (gpointer user_data) } static gboolean -multicast_socket_source_cb (gpointer user_data) +multicast_socket_source_cb (GIOChannel *source, + GIOCondition condition, + gpointer user_data) { GSSDPClient *client; diff --git a/libgssdp/gssdp-socket-functions.c b/libgssdp/gssdp-socket-functions.c new file mode 100644 index 0000000..0fe78de --- /dev/null +++ b/libgssdp/gssdp-socket-functions.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2010 Jens Georg + * + * Author: Jens Georg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include + +#ifdef G_OS_WIN32 + #include + #include + typedef int socklen_t; +#else + #include + #include + #include + #include +#endif + +#include "gssdp-error.h" +#include "gssdp-socket-functions.h" + +static char* +gssdp_socket_error_message (int error) { +#ifdef G_OS_WIN32 + return g_win32_error_message (error); +#else + return g_strdup (g_strerror (error)); +#endif +} + +static int +gssdp_socket_errno () { +#ifdef G_OS_WIN32 + return WSAGetLastError (); +#else + return errno; +#endif +} + + +static gboolean +gssdp_socket_option_set (GSocket *socket, + int level, + int option, + const void *optval, + socklen_t optlen, + GError **error) { + int res; + + res = setsockopt (g_socket_get_fd (socket), + level, + option, + optval, + optlen); + + if (res == -1) { + char *message; + int error_code; + + error_code = gssdp_socket_errno (); + message = gssdp_socket_error_message (error_code); + g_set_error_literal (error, + GSSDP_ERROR, + GSSDP_ERROR_FAILED, + message); + g_free (message); + } + + return res != -1; +} + +gboolean +gssdp_socket_enable_loop (GSocket *socket, + gboolean enable, + GError **error) { + return gssdp_socket_option_set (socket, + IPPROTO_IP, + IP_MULTICAST_LOOP, + (char *) &enable, + sizeof (enable), + error); +} + +gboolean +gssdp_socket_set_ttl (GSocket *socket, + int ttl, + GError **error) { + return gssdp_socket_option_set (socket, + IPPROTO_IP, + IP_MULTICAST_TTL, + (char *) &ttl, + sizeof (ttl), + error); +} + +gboolean +gssdp_socket_enable_broadcast (GSocket *socket, + gboolean enable, + GError **error) { + return gssdp_socket_option_set (socket, + SOL_SOCKET, + SO_BROADCAST, + (char *) &enable, + sizeof (enable), + error); +} + +gboolean +gssdp_socket_mcast_interface_set (GSocket *socket, + GInetAddress *iface_address, + GError **error) { + + const guint8 *address; + gsize native_size; + + address = g_inet_address_to_bytes (iface_address); + native_size = g_inet_address_get_native_size (iface_address); + + return gssdp_socket_option_set (socket, + IPPROTO_IP, + IP_MULTICAST_IF, + (char *) address, + native_size, + error); +} + +/* + * Iface may be NULL if no special interface is wanted + */ +gboolean +gssdp_socket_mcast_group_join (GSocket *socket, + GInetAddress *group, + GInetAddress *iface, + GError **error) { + struct ip_mreq mreq; + GError *inner_error = NULL; + gboolean result; +#ifdef G_OS_WIN32 + GSocketAddress *local_address; +#endif + if (group == NULL || ! G_IS_INET_ADDRESS (group)) { + g_set_error_literal (error, + GSSDP_ERROR, + GSSDP_ERROR_NO_IP_ADDRESS, + "Address is not a valid address"); + + return FALSE; + } + + if (!g_inet_address_get_is_multicast (group)) { + char *address; + + address = g_inet_address_to_string (group); + g_set_error (error, + GSSDP_ERROR, + GSSDP_ERROR_FAILED, + "Address '%s' is not a multicast address", + address); + g_free (address); + + return FALSE; + } + + if (g_inet_address_get_family (group) != G_SOCKET_FAMILY_IPV4) { + g_set_error_literal (error, + GSSDP_ERROR, + GSSDP_ERROR_FAILED, + "IPv6 not supported"); + + return FALSE; + } +#ifdef G_OS_WIN32 + /* On Window, it is only possible to join multicast groups on a bound + * socket + * Note: This test is valid on Windows only. On linux, local_addres + * will be the ANY address (0.0.0.0 for IPv4) + */ + local_address = g_socket_get_local_address (socket, &inner_error); + if (local_address == NULL) { + g_set_error_literal (error, + GSSDP_ERROR, + GSSDP_ERROR_FAILED, + "Cannot join multicast group;" + "socket is not bound"); + + return FALSE; + } + g_object_unref (local_address); +#endif + + memset (&mreq, 0, sizeof (struct ip_mreq)); + memcpy (&(mreq.imr_multiaddr), + g_inet_address_to_bytes (group), + g_inet_address_get_native_size (group)); + + /* if omitted, join will fail if there isn't an explicit multicast + * route or a default route + */ + if (iface != NULL) + memcpy (&(mreq.imr_interface), + g_inet_address_to_bytes (iface), + g_inet_address_get_native_size (iface)); + + result = gssdp_socket_option_set (socket, + IPPROTO_IP, + IP_ADD_MEMBERSHIP, + (char *) &mreq, + sizeof (mreq), + &inner_error); + if (!result) + g_propagate_error (error, inner_error); + + return result; +} + + diff --git a/libgssdp/gssdp-socket-functions.h b/libgssdp/gssdp-socket-functions.h new file mode 100644 index 0000000..e1cda40 --- /dev/null +++ b/libgssdp/gssdp-socket-functions.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Jens Georg + * + * Author: Jens Georg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GSSDP_SOCKET_FUNCTIONS_H +#define __GSSDP_SOCKET_FUNCTIONS_H + +#include + +G_GNUC_INTERNAL gboolean +gssdp_socket_enable_loop (GSocket *socket, + gboolean enable, + GError **error); +G_GNUC_INTERNAL gboolean +gssdp_socket_set_ttl (GSocket *socket, + int ttl, + GError **error); +G_GNUC_INTERNAL gboolean +gssdp_socket_enable_broadcast (GSocket *socket, + gboolean enable, + GError **error); +G_GNUC_INTERNAL gboolean +gssdp_socket_mcast_interface_set (GSocket *socket, + GInetAddress *iface_address, + GError **error); +G_GNUC_INTERNAL gboolean +gssdp_socket_mcast_group_join (GSocket *socket, + GInetAddress *group, + GInetAddress *iface, + GError **error); +#endif diff --git a/libgssdp/gssdp-socket-source.c b/libgssdp/gssdp-socket-source.c index 8922dd3..e52fe48 100644 --- a/libgssdp/gssdp-socket-source.c +++ b/libgssdp/gssdp-socket-source.c @@ -1,10 +1,12 @@ /* * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd. * Copyright (C) 2009 Nokia Corporation, all rights reserved. + * Copyright (C) 2010 Jens Georg * * Author: Jorn Baayen * Zeeshan Ali (Khattak) * + * Jens Georg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,42 +26,11 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "gssdp-socket-functions.h" #include "gssdp-socket-source.h" #include "gssdp-protocol.h" - -struct _GSSDPSocketSource { - GSource source; - - GPollFD poll_fd; -}; - -static gboolean -gssdp_socket_source_prepare (GSource *source, - int *timeout); -static gboolean -gssdp_socket_source_check (GSource *source); -static gboolean -gssdp_socket_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data); -static void -gssdp_socket_source_finalize (GSource *source); - -static const GSourceFuncs gssdp_socket_source_funcs = { - gssdp_socket_source_prepare, - gssdp_socket_source_check, - gssdp_socket_source_dispatch, - gssdp_socket_source_finalize -}; +#include "gssdp-error.h" /** * gssdp_socket_source_new @@ -68,208 +39,185 @@ static const GSourceFuncs gssdp_socket_source_funcs = { **/ GSSDPSocketSource * gssdp_socket_source_new (GSSDPSocketSourceType type, - const char *host_ip) + const char *host_ip, + GError **error) { - GSource *source; - GSSDPSocketSource *socket_source; - struct sockaddr_in bind_addr; - struct in_addr iface_addr; - struct ip_mreq mreq; - gboolean boolean = TRUE; - guchar ttl = 4; - int res; + GSSDPSocketSource *socket_source = NULL; + GInetAddress *iface_address = NULL; + GSocketAddress *bind_address = NULL; + GInetAddress *group = NULL; + GError *inner_error = NULL; + GSocketFamily family; + + iface_address = g_inet_address_new_from_string (host_ip); + if (iface_address == NULL) { + if (error != NULL) { + *error = g_error_new (GSSDP_ERROR, + GSSDP_ERROR_FAILED, + "Invalid host ip: %s", + host_ip); + inner_error = *error; + } - /* Create source */ - source = g_source_new ((GSourceFuncs*)&gssdp_socket_source_funcs, - sizeof (GSSDPSocketSource)); + goto error; + } - socket_source = (GSSDPSocketSource *) source; + family = g_inet_address_get_family (iface_address); + + if (family == G_SOCKET_FAMILY_IPV4) + group = g_inet_address_new_from_string (SSDP_ADDR); + else { + if (error != NULL) { + *error = g_error_new_literal (GSSDP_ERROR, + GSSDP_ERROR_FAILED, + "IPv6 address"); + inner_error = *error; + } - /* Create socket */ - socket_source->poll_fd.fd = socket (AF_INET, - SOCK_DGRAM, - IPPROTO_UDP); - if (socket_source->poll_fd.fd == -1) goto error; - - socket_source->poll_fd.events = G_IO_IN | G_IO_ERR; + } - g_source_add_poll (source, &socket_source->poll_fd); - /* Enable broadcasting */ - res = setsockopt (socket_source->poll_fd.fd, - SOL_SOCKET, - SO_BROADCAST, - &boolean, - sizeof (boolean)); - if (res == -1) - goto error; + /* Create source */ + socket_source = g_slice_new0 (GSSDPSocketSource); - /* TTL */ - res = setsockopt (socket_source->poll_fd.fd, - IPPROTO_IP, - IP_MULTICAST_TTL, - &ttl, - sizeof (ttl)); - if (res == -1) - goto error; + /* Create socket */ + socket_source->socket = g_socket_new (G_SOCKET_FAMILY_IPV4, + G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_PROTOCOL_UDP, + &inner_error); - memset (&bind_addr, 0, sizeof (bind_addr)); - bind_addr.sin_family = AF_INET; + if (!socket_source->socket) { + g_propagate_prefixed_error (error, + inner_error, + "Could not create socket"); - res = inet_aton (host_ip, &iface_addr); - if (res == 0) goto error; + } + + /* Enable broadcasting */ + if (!gssdp_socket_enable_broadcast (socket_source->socket, + TRUE, + &inner_error)) { + g_propagate_prefixed_error (error, + inner_error, + "Failed to enable broadcast"); + goto error; + } + + /* TTL */ + if (!gssdp_socket_set_ttl (socket_source->socket, + 4, + &inner_error)) { + g_propagate_prefixed_error (error, + inner_error, + "Failed to set TTL"); + } /* Set up additional things according to the type of socket desired */ if (type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) { - /* Allow multiple sockets to use the same PORT number */ - res = setsockopt (socket_source->poll_fd.fd, - SOL_SOCKET, -#ifdef SO_REUSEPORT - SO_REUSEPORT, -#else - SO_REUSEADDR, -#endif - &boolean, - sizeof (boolean)); - if (res == -1) - goto error; - /* Enable multicast loopback */ - res = setsockopt (socket_source->poll_fd.fd, - IPPROTO_IP, - IP_MULTICAST_LOOP, - &boolean, - sizeof (boolean)); - if (res == -1) - goto error; - - /* Set the interface */ - res = setsockopt (socket_source->poll_fd.fd, - IPPROTO_IP, - IP_MULTICAST_IF, - &iface_addr, - sizeof (struct in_addr)); - if (res == -1) - goto error; + if (!gssdp_socket_enable_loop (socket_source->socket, + TRUE, + &inner_error)) { + g_propagate_prefixed_error ( + error, + inner_error, + "Failed to enable loop-back"); - /* Subscribe to multicast channel */ - res = inet_aton (SSDP_ADDR, &(mreq.imr_multiaddr)); - if (res == 0) goto error; + } - memcpy (&(mreq.imr_interface), - &iface_addr, - sizeof (struct in_addr)); + if (!gssdp_socket_mcast_interface_set (socket_source->socket, + iface_address, + &inner_error)) { + g_propagate_prefixed_error ( + error, + inner_error, + "Failed to set multicast interface"); - res = setsockopt (socket_source->poll_fd.fd, - IPPROTO_IP, - IP_ADD_MEMBERSHIP, - &mreq, - sizeof (mreq)); - if (res == -1) goto error; + } - bind_addr.sin_port = htons (SSDP_PORT); - res = inet_aton (SSDP_ADDR, &(bind_addr.sin_addr)); - if (res == 0) - goto error; +#ifdef G_OS_WIN32 + bind_address = g_inet_socket_address_new (iface_address, + SSDP_PORT); +#else + bind_address = g_inet_socket_address_new (group, + SSDP_PORT); +#endif } else { - bind_addr.sin_port = 0; - memcpy (&(bind_addr.sin_addr), - &iface_addr, - sizeof (struct in_addr)); + bind_address = g_inet_socket_address_new (iface_address, + SSDP_PORT); } /* Bind to requested port and address */ - res = bind (socket_source->poll_fd.fd, - (struct sockaddr *) &bind_addr, - sizeof (bind_addr)); - if (res == -1) - goto error; - - return socket_source; - -error: - g_source_destroy (source); - - return NULL; -} - -static gboolean -gssdp_socket_source_prepare (GSource *source, - int *timeout) -{ - return FALSE; -} - -static gboolean -gssdp_socket_source_check (GSource *source) -{ - GSSDPSocketSource *socket_source; + if (!g_socket_bind (socket_source->socket, + bind_address, + TRUE, + &inner_error)) { + g_propagate_prefixed_error (error, + inner_error, + "Failed to bind socket"); - socket_source = (GSSDPSocketSource *) source; - - return socket_source->poll_fd.revents & (G_IO_IN | G_IO_ERR); -} - -static gboolean -gssdp_socket_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - GSSDPSocketSource *socket_source; + goto error; + } - socket_source = (GSSDPSocketSource *) source; + if (type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) { - if (socket_source->poll_fd.revents & G_IO_IN) { - /* Ready to read */ - if (callback) - callback (user_data); - } else if (socket_source->poll_fd.revents & G_IO_ERR) { - /* Error */ - int value; - socklen_t size_int; + /* Subscribe to multicast channel */ + if (!gssdp_socket_mcast_group_join (socket_source->socket, + group, + iface_address, + &inner_error)) { + char *address = g_inet_address_to_string (group); + g_propagate_prefixed_error (error, + inner_error, + "Failed to join group %s", + address); + g_free (address); - value = EINVAL; - size_int = sizeof (int); - - /* Get errno from socket */ - getsockopt (socket_source->poll_fd.fd, - SOL_SOCKET, - SO_ERROR, - &value, - &size_int); + goto error; + } + } - g_warning ("Socket error %d received: %s", - value, - strerror (value)); + socket_source->source = g_socket_create_source (socket_source->socket, + G_IO_IN | G_IO_ERR, + NULL); +error: + if (iface_address != NULL) + g_object_unref (iface_address); + if (bind_address != NULL) + g_object_unref (bind_address); + if (group != NULL) + g_object_unref (group); + if (inner_error != NULL) { + if (error == NULL) { + g_warning ("Failed to create socket source: %s", + inner_error->message); + g_error_free (inner_error); + } + + return NULL; } - return TRUE; + return socket_source; } -static void -gssdp_socket_source_finalize (GSource *source) +GSocket * +gssdp_socket_source_get_socket (GSSDPSocketSource *socket_source) { - GSSDPSocketSource *socket_source; + g_return_val_if_fail (socket_source != NULL, NULL); - socket_source = (GSSDPSocketSource *) source; - - /* Close the socket */ - close (socket_source->poll_fd.fd); + return socket_source->socket; } -/** - * gssdp_socket_source_get_fd - * - * Return value: The socket's FD. - **/ -int -gssdp_socket_source_get_fd (GSSDPSocketSource *socket_source) +void +gssdp_socket_source_destroy (GSSDPSocketSource *socket_source) { - g_return_val_if_fail (socket_source != NULL, -1); - - return socket_source->poll_fd.fd; + g_return_if_fail (socket_source != NULL); + g_source_destroy (socket_source->source); + g_socket_close (socket_source->socket, NULL); + g_object_unref (socket_source->socket); + g_slice_free (GSSDPSocketSource, socket_source); } diff --git a/libgssdp/gssdp-socket-source.h b/libgssdp/gssdp-socket-source.h index c8a3594..c5c7369 100644 --- a/libgssdp/gssdp-socket-source.h +++ b/libgssdp/gssdp-socket-source.h @@ -1,7 +1,9 @@ /* * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd. + * Copyright (C) 2010 Jens Georg * * Author: Jorn Baayen + * Jens Georg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -22,8 +24,15 @@ #ifndef __GSSDP_SOCKET_SOURCE_H__ #define __GSSDP_SOCKET_SOURCE_H__ +#include + G_BEGIN_DECLS +struct _GSSDPSocketSource { + GSource *source; + GSocket *socket; +}; + typedef struct _GSSDPSocketSource GSSDPSocketSource; typedef enum { @@ -32,11 +41,14 @@ typedef enum { } GSSDPSocketSourceType; G_GNUC_INTERNAL GSSDPSocketSource * -gssdp_socket_source_new (GSSDPSocketSourceType type, - const char *host_ip); - -G_GNUC_INTERNAL int -gssdp_socket_source_get_fd (GSSDPSocketSource *socket_source); +gssdp_socket_source_new (GSSDPSocketSourceType type, + const char *host_ip, + GError **error); +G_GNUC_INTERNAL void +gssdp_socket_source_destroy (GSSDPSocketSource *socket_source); + +G_GNUC_INTERNAL GSocket* +gssdp_socket_source_get_socket (GSSDPSocketSource *socket_source); G_END_DECLS