2 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3 * Copyright (C) 2009 Nokia Corporation, all rights reserved.
5 * Author: Jorn Baayen <jorn@openedhand.com>
6 * Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
7 * <zeeshan.ali@nokia.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
26 * SECTION:gssdp-client
27 * @short_description: SSDP "bus" wrapper.
29 * #GSSDPClient wraps the SSDP "bus" as used by both #GSSDPResourceBrowser
30 * and #GSSDPResourceGroup.
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <sys/utsname.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
43 #include <arpa/inet.h>
46 #include <libsoup/soup-headers.h>
48 #include "gssdp-client.h"
49 #include "gssdp-client-private.h"
50 #include "gssdp-error.h"
51 #include "gssdp-socket-source.h"
52 #include "gssdp-marshal.h"
53 #include "gssdp-protocol.h"
55 /* Size of the buffer used for reading from the socket */
58 G_DEFINE_TYPE (GSSDPClient,
62 struct _GSSDPClientPrivate {
63 GMainContext *main_context;
71 GSSDPSocketSource *request_socket;
72 GSSDPSocketSource *multicast_socket;
92 static guint signals[LAST_SIGNAL];
94 /* Function prototypes */
96 gssdp_client_set_main_context (GSSDPClient *client,
97 GMainContext *context);
99 make_server_id (void);
101 request_socket_source_cb (gpointer user_data);
103 multicast_socket_source_cb (gpointer user_data);
105 init_network_info (GSSDPClient *client);
108 gssdp_client_init (GSSDPClient *client)
110 client->priv = G_TYPE_INSTANCE_GET_PRIVATE
115 client->priv->active = TRUE;
117 /* Generate default server ID */
118 client->priv->server_id = make_server_id ();
122 gssdp_client_constructed (GObject *object)
124 GSSDPClient *client = GSSDP_CLIENT (object);
126 /* Make sure all network info is available to us */
127 if (!init_network_info (client))
130 /* Set up sockets (Will set errno if it failed) */
131 client->priv->request_socket =
132 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_REQUEST,
133 gssdp_client_get_host_ip (client));
134 if (client->priv->request_socket != NULL) {
135 g_source_set_callback
136 ((GSource *) client->priv->request_socket,
137 request_socket_source_cb,
144 client->priv->multicast_socket =
145 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_MULTICAST,
146 gssdp_client_get_host_ip (client));
147 if (client->priv->multicast_socket != NULL) {
148 g_source_set_callback
149 ((GSource *) client->priv->multicast_socket,
150 multicast_socket_source_cb,
156 if (!client->priv->request_socket || !client->priv->multicast_socket) {
157 if (client->priv->error)
158 g_set_error_literal (client->priv->error,
166 g_source_attach ((GSource *) client->priv->request_socket,
167 client->priv->main_context);
168 g_source_unref ((GSource *) client->priv->request_socket);
170 g_source_attach ((GSource *) client->priv->multicast_socket,
171 client->priv->main_context);
172 g_source_unref ((GSource *) client->priv->multicast_socket);
176 gssdp_client_get_property (GObject *object,
183 client = GSSDP_CLIENT (object);
185 switch (property_id) {
189 gssdp_client_get_server_id (client));
191 case PROP_MAIN_CONTEXT:
195 gssdp_client_get_main_context (client));
198 g_value_set_string (value,
199 gssdp_client_get_interface (client));
202 g_value_set_string (value,
203 gssdp_client_get_host_ip (client));
206 g_value_set_boolean (value, client->priv->active);
209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
215 gssdp_client_set_property (GObject *object,
222 client = GSSDP_CLIENT (object);
224 switch (property_id) {
226 gssdp_client_set_server_id (client,
227 g_value_get_string (value));
229 case PROP_MAIN_CONTEXT:
230 gssdp_client_set_main_context (client,
231 g_value_get_pointer (value));
234 client->priv->error = g_value_get_pointer (value);
237 client->priv->interface = g_value_dup_string (value);
240 client->priv->active = g_value_get_boolean (value);
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
249 gssdp_client_dispose (GObject *object)
253 client = GSSDP_CLIENT (object);
255 /* Destroy the SocketSources */
256 if (client->priv->request_socket) {
257 g_source_destroy ((GSource *) client->priv->request_socket);
258 client->priv->request_socket = NULL;
261 if (client->priv->multicast_socket) {
262 g_source_destroy ((GSource *) client->priv->multicast_socket);
263 client->priv->multicast_socket = NULL;
266 /* Unref the context */
267 if (client->priv->main_context) {
268 g_main_context_unref (client->priv->main_context);
269 client->priv->main_context = NULL;
272 G_OBJECT_CLASS (gssdp_client_parent_class)->dispose (object);
276 gssdp_client_finalize (GObject *object)
280 client = GSSDP_CLIENT (object);
282 g_free (client->priv->server_id);
283 g_free (client->priv->interface);
284 g_free (client->priv->host_ip);
286 G_OBJECT_CLASS (gssdp_client_parent_class)->finalize (object);
290 gssdp_client_class_init (GSSDPClientClass *klass)
292 GObjectClass *object_class;
294 object_class = G_OBJECT_CLASS (klass);
296 object_class->constructed = gssdp_client_constructed;
297 object_class->set_property = gssdp_client_set_property;
298 object_class->get_property = gssdp_client_get_property;
299 object_class->dispose = gssdp_client_dispose;
300 object_class->finalize = gssdp_client_finalize;
302 g_type_class_add_private (klass, sizeof (GSSDPClientPrivate));
305 * GSSDPClient:server-id
307 * The SSDP server's identifier.
309 g_object_class_install_property
315 "The SSDP server's identifier.",
318 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
319 G_PARAM_STATIC_BLURB));
322 * GSSDPClient:main-context
324 * The #GMainContext to use. Set to NULL to use the default.
326 g_object_class_install_property
332 "The associated GMainContext.",
333 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
334 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
335 G_PARAM_STATIC_BLURB));
344 g_object_class_install_property
350 "Location where to store the constructor GError, "
352 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
353 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
354 G_PARAM_STATIC_BLURB));
357 * GSSDPClient:interface
359 * The name of the network interface this client is associated with.
360 * Set to NULL to autodetect.
362 g_object_class_install_property
368 "The name of the associated network interface.",
371 G_PARAM_CONSTRUCT_ONLY |
372 G_PARAM_STATIC_NAME |
373 G_PARAM_STATIC_NICK |
374 G_PARAM_STATIC_BLURB));
377 * GSSDPClient:host-ip
379 * The IP address of the assoicated network interface.
381 g_object_class_install_property
384 g_param_spec_string ("host-ip",
386 "The IP address of the associated"
390 G_PARAM_STATIC_NAME |
391 G_PARAM_STATIC_NICK |
392 G_PARAM_STATIC_BLURB));
397 * Whether this client is active or not (passive). When active
398 * (default), the client sends messages on the network, otherwise
399 * not. In most cases, you don't want to touch this property.
402 g_object_class_install_property
408 "TRUE if the client is active.",
411 G_PARAM_STATIC_NAME |
412 G_PARAM_STATIC_NICK |
413 G_PARAM_STATIC_BLURB));
416 * GSSDPClient::message-received
422 signals[MESSAGE_RECEIVED] =
423 g_signal_new ("message-received",
428 gssdp_marshal_VOID__STRING_UINT_INT_POINTER,
431 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
439 * @main_context: The #GMainContext to associate with, or NULL
440 * @interface: The name of the network interface, or %NULL for auto-detection.
441 * @error: Location to store error, or NULL
443 * Return value: A new #GSSDPClient object.
446 gssdp_client_new (GMainContext *main_context,
447 const char *interface,
450 return g_object_new (GSSDP_TYPE_CLIENT,
451 "main-context", main_context,
452 "interface", interface,
458 * Sets the GMainContext @client is associated with to @main_context
461 gssdp_client_set_main_context (GSSDPClient *client,
462 GMainContext *main_context)
464 g_return_if_fail (GSSDP_IS_CLIENT (client));
466 /* A NULL main_context is fine */
468 client->priv->main_context = g_main_context_ref (main_context);
470 g_object_notify (G_OBJECT (client), "main-context");
474 * gssdp_client_get_main_context
475 * @client: A #GSSDPClient
477 * Return value: The #GMainContext @client is associated with, or NULL.
480 gssdp_client_get_main_context (GSSDPClient *client)
482 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
484 return client->priv->main_context;
488 * gssdp_client_set_server_id
489 * @client: A #GSSDPClient
490 * @server_id: The server ID
492 * Sets the server ID of @client to @server_id.
495 gssdp_client_set_server_id (GSSDPClient *client,
496 const char *server_id)
498 g_return_if_fail (GSSDP_IS_CLIENT (client));
500 if (client->priv->server_id) {
501 g_free (client->priv->server_id);
502 client->priv->server_id = NULL;
506 client->priv->server_id = g_strdup (server_id);
508 g_object_notify (G_OBJECT (client), "server-id");
512 * gssdp_client_get_server_id
513 * @client: A #GSSDPClient
515 * Return value: The server ID.
518 gssdp_client_get_server_id (GSSDPClient *client)
520 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
522 return client->priv->server_id;
526 * gssdp_client_get_interface
527 * @client: A #GSSDPClient
529 * Get the name of the network interface associated to @client.
531 * Return value: The network interface name. This string should not be freed.
534 gssdp_client_get_interface (GSSDPClient *client)
536 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
538 return client->priv->interface;
542 * gssdp_client_get_host_ip
543 * @client: A #GSSDPClient
545 * Get the IP address we advertise ourselves as using.
547 * Return value: The IP address. This string should not be freed.
550 gssdp_client_get_host_ip (GSSDPClient *client)
552 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
554 return client->priv->host_ip;
558 * gssdp_client_get_active
559 * @client: A #GSSDPClient
561 * Return value: %TRUE if @client is active, %FALSE otherwise.
564 gssdp_client_get_active (GSSDPClient *client)
566 g_return_val_if_fail (GSSDP_IS_CLIENT (client), FALSE);
568 return client->priv->active;
572 * _gssdp_client_send_message
573 * @client: A #GSSDPClient
574 * @dest_ip: The destination IP address, or NULL to broadcast
575 * @dest_port: The destination port, or NULL for default
576 * @message: The message to send
578 * Sends @message to @dest_ip.
581 _gssdp_client_send_message (GSSDPClient *client,
586 struct sockaddr_in addr;
589 g_return_if_fail (GSSDP_IS_CLIENT (client));
590 g_return_if_fail (message != NULL);
592 if (!client->priv->active)
593 /* We don't send messages in passive mode */
596 /* Broadcast if @dest_ip is NULL */
600 /* Use default port if no port was explicitly specified */
602 dest_port = SSDP_PORT;
604 socket_fd = gssdp_socket_source_get_fd (client->priv->request_socket);
606 memset (&addr, 0, sizeof (addr));
608 addr.sin_family = AF_INET;
609 addr.sin_port = htons (dest_port);
610 addr.sin_addr.s_addr = inet_addr (dest_ip);
612 res = sendto (socket_fd,
616 (struct sockaddr *) &addr,
620 g_warning ("sendto: Error %d sending message: %s",
621 errno, strerror (errno));
626 * Generates the default server ID
629 make_server_id (void)
631 struct utsname sysinfo;
635 return g_strdup_printf ("%s/%s GSSDP/%s",
642 parse_http_request (char *buf,
644 SoupMessageHeaders **headers,
649 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
651 if (soup_headers_parse_request (buf,
656 NULL) == SOUP_STATUS_OK) {
657 if (g_ascii_strncasecmp (req_method,
659 strlen (SSDP_SEARCH_METHOD)) == 0)
660 *type = _GSSDP_DISCOVERY_REQUEST;
661 else if (g_ascii_strncasecmp (req_method,
663 strlen (GENA_NOTIFY_METHOD)) == 0)
664 *type = _GSSDP_ANNOUNCEMENT;
666 g_warning ("Unhandled method '%s'", req_method);
672 soup_message_headers_free (*headers);
680 parse_http_response (char *buf,
682 SoupMessageHeaders **headers,
687 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
689 if (soup_headers_parse_response (buf,
695 if (status_code == 200)
696 *type = _GSSDP_DISCOVERY_RESPONSE;
698 g_warning ("Unhandled status code '%d'", status_code);
702 soup_message_headers_free (*headers);
710 * Called when data can be read from the socket
713 socket_source_cb (GSSDPSocketSource *socket, GSSDPClient *client)
717 char buf[BUF_SIZE], *end;
718 struct sockaddr_in addr;
720 SoupMessageHeaders *headers;
721 struct in_addr our_addr;
722 in_addr_t our_network;
723 in_addr_t recv_network;
726 fd = gssdp_socket_source_get_fd (socket);
729 addr_size = sizeof (addr);
731 bytes = recvfrom (fd,
733 BUF_SIZE - 1, /* Leave space for trailing \0 */
735 (struct sockaddr *) &addr,
738 g_warning ("Failed to read from socket: %d (%s)",
745 /* We need the following lines to make sure the right client received
746 * the packet. We won't need to do this if there was any way to tell
747 * Mr. Unix that we are only interested in receiving multicast packets
748 * on this socket from a particular interface but AFAIK that is not
749 * possible, at least not in a portable way.
751 recv_network = inet_netof (addr.sin_addr);
752 our_addr.s_addr = inet_addr (gssdp_client_get_host_ip (client));
753 our_network = inet_netof (our_addr);
754 if (recv_network != our_network)
757 if (bytes >= BUF_SIZE) {
758 g_warning ("Received packet of %u bytes, but the maximum "
759 "buffer size is %d. Packed dropped.",
760 (unsigned int) bytes, BUF_SIZE);
765 /* Add trailing \0 */
769 end = strstr (buf, "\r\n\r\n");
771 g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. "
783 if (!parse_http_request (buf,
787 if (!parse_http_response (buf,
791 g_warning ("Unhandled message '%s'", buf);
795 /* Emit signal if parsing succeeded */
797 g_signal_emit (client,
798 signals[MESSAGE_RECEIVED],
800 inet_ntoa (addr.sin_addr),
801 ntohs (addr.sin_port),
807 soup_message_headers_free (headers);
813 request_socket_source_cb (gpointer user_data)
817 client = GSSDP_CLIENT (user_data);
819 return socket_source_cb (client->priv->request_socket, client);
823 multicast_socket_source_cb (gpointer user_data)
827 client = GSSDP_CLIENT (user_data);
829 return socket_source_cb (client->priv->multicast_socket, client);
833 * Get the host IP for the specified interface. If no interface is specified,
834 * it gets the IP of the first up & running interface and sets @interface
838 get_host_ip (char **interface)
840 struct ifaddrs *ifa_list, *ifa;
842 GList *up_ifaces, *iface;
847 if (getifaddrs (&ifa_list) != 0) {
848 g_error ("Failed to retrieve list of network interfaces:\n%s\n",
854 for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) {
855 if (ifa->ifa_addr == NULL)
858 if (*interface && strcmp (*interface, ifa->ifa_name) != 0)
860 else if (!(ifa->ifa_flags & IFF_UP))
862 else if ((ifa->ifa_flags & IFF_POINTOPOINT))
865 /* Loopback and IPv6 interfaces go at the bottom on the list */
866 if (ifa->ifa_flags & IFF_LOOPBACK ||
867 ifa->ifa_addr->sa_family == AF_INET6)
868 up_ifaces = g_list_append (up_ifaces, ifa);
870 up_ifaces = g_list_prepend (up_ifaces, ifa);
873 for (iface = up_ifaces; iface != NULL; iface = iface->next) {
874 char ip[INET6_ADDRSTRLEN];
876 struct sockaddr_in *s4;
877 struct sockaddr_in6 *s6;
883 switch (ifa->ifa_addr->sa_family) {
885 s4 = (struct sockaddr_in *) ifa->ifa_addr;
886 p = inet_ntop (AF_INET,
887 &s4->sin_addr, ip, sizeof (ip));
890 s6 = (struct sockaddr_in6 *) ifa->ifa_addr;
891 p = inet_ntop (AF_INET6,
892 &s6->sin6_addr, ip, sizeof (ip));
895 continue; /* Unknown: ignore */
901 if (*interface == NULL)
902 *interface = g_strdup (ifa->ifa_name);
907 g_list_free (up_ifaces);
908 freeifaddrs (ifa_list);
914 init_network_info (GSSDPClient *client)
918 if (client->priv->interface == NULL || client->priv->host_ip == NULL)
919 client->priv->host_ip =
920 get_host_ip (&client->priv->interface);
922 if (client->priv->interface == NULL) {
923 if (client->priv->error)
924 g_set_error (client->priv->error,
927 "No default route?");
930 } else if (client->priv->host_ip == NULL) {
931 if (client->priv->error)
932 g_set_error (client->priv->error,
934 GSSDP_ERROR_NO_IP_ADDRESS,
935 "Failed to find IP of interface %s",
936 client->priv->interface);