2 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3 * Copyright (C) 2009 Nokia Corporation.
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., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, 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/types.h>
37 #include <sys/socket.h>
38 #include <sys/utsname.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
42 #define _WIN32_WINNT 0x502
46 typedef int socklen_t;
47 /* from the return value of inet_addr */
48 typedef unsigned long in_addr_t;
55 #include <arpa/inet.h>
59 #include <libsoup/soup-headers.h>
61 #include "gssdp-client.h"
62 #include "gssdp-client-private.h"
63 #include "gssdp-error.h"
64 #include "gssdp-socket-source.h"
65 #include "gssdp-marshal.h"
66 #include "gssdp-protocol.h"
68 #ifndef INET6_ADDRSTRLEN
69 #define INET6_ADDRSTRLEN 46
72 /* Size of the buffer used for reading from the socket */
73 #define BUF_SIZE 65536
76 gssdp_client_initable_iface_init (gpointer g_iface,
79 G_DEFINE_TYPE_EXTENDED (GSSDPClient,
85 gssdp_client_initable_iface_init));
87 struct _GSSDPClientPrivate {
93 GSSDPSocketSource *request_socket;
94 GSSDPSocketSource *multicast_socket;
95 GSSDPSocketSource *search_socket;
116 static guint signals[LAST_SIGNAL];
119 make_server_id (void);
121 request_socket_source_cb (GIOChannel *source,
122 GIOCondition condition,
125 multicast_socket_source_cb (GIOChannel *source,
126 GIOCondition condition,
129 search_socket_source_cb (GIOChannel *source,
130 GIOCondition condition,
134 init_network_info (GSSDPClient *client,
138 gssdp_client_initable_init (GInitable *initable,
139 GCancellable *cancellable,
143 gssdp_client_init (GSSDPClient *client)
145 client->priv = G_TYPE_INSTANCE_GET_PRIVATE
150 client->priv->active = TRUE;
152 /* Generate default server ID */
153 client->priv->server_id = make_server_id ();
157 gssdp_client_initable_iface_init (gpointer g_iface,
160 GInitableIface *iface = (GInitableIface *)g_iface;
161 iface->init = gssdp_client_initable_init;
165 gssdp_client_initable_init (GInitable *initable,
166 GCancellable *cancellable,
169 GSSDPClient *client = GSSDP_CLIENT (initable);
170 GError *internal_error = NULL;
172 if (client->priv->initialized)
176 WSADATA wsaData = {0};
177 if (WSAStartup (MAKEWORD (2,2), &wsaData) != 0) {
180 message = g_win32_error_message (WSAGetLastError ());
181 g_set_error_literal (error,
191 /* Make sure all network info is available to us */
192 if (!init_network_info (client, &internal_error))
195 /* Set up sockets (Will set errno if it failed) */
196 client->priv->request_socket =
197 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_REQUEST,
198 gssdp_client_get_host_ip (client),
200 if (client->priv->request_socket != NULL) {
201 gssdp_socket_source_set_callback
202 (client->priv->request_socket,
203 (GSourceFunc) request_socket_source_cb,
209 client->priv->multicast_socket =
210 gssdp_socket_source_new (GSSDP_SOCKET_SOURCE_TYPE_MULTICAST,
211 gssdp_client_get_host_ip (client),
213 if (client->priv->multicast_socket != NULL) {
214 gssdp_socket_source_set_callback
215 (client->priv->multicast_socket,
216 (GSourceFunc) multicast_socket_source_cb,
222 /* Setup send socket. For security reasons, it is not recommended to
223 * send M-SEARCH with source port == SSDP_PORT */
224 client->priv->search_socket = gssdp_socket_source_new
225 (GSSDP_SOCKET_SOURCE_TYPE_SEARCH,
226 gssdp_client_get_host_ip (client),
228 if (client->priv->search_socket != NULL) {
229 gssdp_socket_source_set_callback
230 (client->priv->search_socket,
231 (GSourceFunc) search_socket_source_cb,
235 if (!client->priv->request_socket ||
236 !client->priv->multicast_socket ||
237 !client->priv->search_socket) {
238 g_propagate_error (error, internal_error);
240 if (client->priv->request_socket) {
241 g_object_unref (client->priv->request_socket);
243 client->priv->request_socket = NULL;
246 if (client->priv->multicast_socket) {
247 g_object_unref (client->priv->multicast_socket);
249 client->priv->multicast_socket = NULL;
252 if (client->priv->search_socket) {
253 g_object_unref (client->priv->search_socket);
255 client->priv->search_socket = NULL;
261 gssdp_socket_source_attach (client->priv->request_socket);
262 gssdp_socket_source_attach (client->priv->multicast_socket);
263 gssdp_socket_source_attach (client->priv->search_socket);
265 client->priv->initialized = TRUE;
271 gssdp_client_get_property (GObject *object,
278 client = GSSDP_CLIENT (object);
280 switch (property_id) {
284 gssdp_client_get_server_id (client));
286 case PROP_MAIN_CONTEXT:
287 g_warning ("GSSDPClient:main-context is deprecated."
288 " Please use g_main_context_push_thread_default()");
292 g_main_context_get_thread_default ());
295 g_value_set_string (value,
296 gssdp_client_get_interface (client));
299 g_value_set_string (value,
300 gssdp_client_get_network (client));
303 g_value_set_string (value,
304 gssdp_client_get_host_ip (client));
307 g_value_set_boolean (value, client->priv->active);
310 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
316 gssdp_client_set_property (GObject *object,
323 client = GSSDP_CLIENT (object);
325 switch (property_id) {
327 gssdp_client_set_server_id (client,
328 g_value_get_string (value));
330 case PROP_MAIN_CONTEXT:
331 if (g_value_get_pointer (value) != NULL)
332 g_warning ("GSSDPClient:main-context is deprecated."
333 " Please use g_main_context_push_thread_default()");
336 client->priv->iface = g_value_dup_string (value);
339 client->priv->network = g_value_dup_string (value);
342 client->priv->active = g_value_get_boolean (value);
345 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
351 gssdp_client_dispose (GObject *object)
355 client = GSSDP_CLIENT (object);
357 /* Destroy the SocketSources */
358 if (client->priv->request_socket) {
359 g_object_unref (client->priv->request_socket);
360 client->priv->request_socket = NULL;
363 if (client->priv->multicast_socket) {
364 g_object_unref (client->priv->multicast_socket);
365 client->priv->multicast_socket = NULL;
368 if (client->priv->search_socket) {
369 g_object_unref (client->priv->search_socket);
370 client->priv->search_socket = NULL;
373 G_OBJECT_CLASS (gssdp_client_parent_class)->dispose (object);
377 gssdp_client_finalize (GObject *object)
381 client = GSSDP_CLIENT (object);
386 g_free (client->priv->server_id);
387 g_free (client->priv->iface);
388 g_free (client->priv->host_ip);
389 g_free (client->priv->network);
391 G_OBJECT_CLASS (gssdp_client_parent_class)->finalize (object);
395 gssdp_client_class_init (GSSDPClientClass *klass)
397 GObjectClass *object_class;
399 object_class = G_OBJECT_CLASS (klass);
401 object_class->set_property = gssdp_client_set_property;
402 object_class->get_property = gssdp_client_get_property;
403 object_class->dispose = gssdp_client_dispose;
404 object_class->finalize = gssdp_client_finalize;
406 g_type_class_add_private (klass, sizeof (GSSDPClientPrivate));
409 * GSSDPClient:server-id:
411 * The SSDP server's identifier.
413 g_object_class_install_property
419 "The SSDP server's identifier.",
422 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
423 G_PARAM_STATIC_BLURB));
426 * GSSDPClient:main-context: (skip)
428 * The #GMainContext to use. Set to NULL to use the default.
429 * Deprecated: 0.11.2: Use g_main_context_push_thread_default().
431 g_object_class_install_property
437 "The associated GMainContext.",
438 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
439 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
440 G_PARAM_STATIC_BLURB));
443 * GSSDPClient:interface:
445 * The name of the network interface this client is associated with.
446 * Set to NULL to autodetect.
448 g_object_class_install_property
454 "The name of the associated network interface.",
457 G_PARAM_CONSTRUCT_ONLY |
458 G_PARAM_STATIC_NAME |
459 G_PARAM_STATIC_NICK |
460 G_PARAM_STATIC_BLURB));
463 * GSSDPClient:network:
465 * The network this client is currently connected to. You could set this
466 * to anything you want to identify the network this client is
467 * associated with. If you are using #GUPnPContextManager and associated
468 * interface is a WiFi interface, this property is set to the ESSID of
469 * the network. Otherwise, expect this to be the network IP address by
472 g_object_class_install_property
478 "The network this client is currently connected to.",
482 G_PARAM_STATIC_NAME |
483 G_PARAM_STATIC_NICK |
484 G_PARAM_STATIC_BLURB));
487 * GSSDPClient:host-ip:
489 * The IP address of the assoicated network interface.
491 g_object_class_install_property
494 g_param_spec_string ("host-ip",
496 "The IP address of the associated"
500 G_PARAM_STATIC_NAME |
501 G_PARAM_STATIC_NICK |
502 G_PARAM_STATIC_BLURB));
505 * GSSDPClient:active:
507 * Whether this client is active or not (passive). When active
508 * (default), the client sends messages on the network, otherwise
509 * not. In most cases, you don't want to touch this property.
512 g_object_class_install_property
518 "TRUE if the client is active.",
521 G_PARAM_STATIC_NAME |
522 G_PARAM_STATIC_NICK |
523 G_PARAM_STATIC_BLURB));
526 * GSSDPClient::message-received: (skip)
532 signals[MESSAGE_RECEIVED] =
533 g_signal_new ("message-received",
538 gssdp_marshal_VOID__STRING_UINT_INT_POINTER,
541 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
549 * @main_context: (allow-none): Deprecated: 0.11.2: Always set to NULL. If you want to
550 * specify a context use g_main_context_push_thread_default()
551 * @iface: The name of the network interface, or %NULL for auto-detection.
552 * @error: Location to store error, or NULL
554 * Return value: A new #GSSDPClient object.
557 gssdp_client_new (GMainContext *main_context,
562 g_warning ("GSSDPClient:main-context is deprecated."
563 " Please use g_main_context_push_thread_default()");
566 return g_initable_new (GSSDP_TYPE_CLIENT,
574 * gssdp_client_get_main_context: (skip)
575 * @client: A #GSSDPClient
577 * Returns: (transfer none): The #GMainContext @client is associated with, or NULL.
578 * Deprecated: 0.11.2: Returns g_main_context_get_thread_default()
581 gssdp_client_get_main_context (GSSDPClient *client)
583 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
585 return g_main_context_get_thread_default ();
589 * gssdp_client_set_server_id:
590 * @client: A #GSSDPClient
591 * @server_id: The server ID
593 * Sets the server ID of @client to @server_id.
596 gssdp_client_set_server_id (GSSDPClient *client,
597 const char *server_id)
599 g_return_if_fail (GSSDP_IS_CLIENT (client));
601 if (client->priv->server_id) {
602 g_free (client->priv->server_id);
603 client->priv->server_id = NULL;
607 client->priv->server_id = g_strdup (server_id);
609 g_object_notify (G_OBJECT (client), "server-id");
613 * gssdp_client_get_server_id:
614 * @client: A #GSSDPClient
616 * Return value: The server ID.
619 gssdp_client_get_server_id (GSSDPClient *client)
621 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
623 return client->priv->server_id;
627 * gssdp_client_get_interface:
628 * @client: A #GSSDPClient
630 * Get the name of the network interface associated to @client.
632 * Return value: The network interface name. This string should not be freed.
635 gssdp_client_get_interface (GSSDPClient *client)
637 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
639 return client->priv->iface;
643 * gssdp_client_get_host_ip:
644 * @client: A #GSSDPClient
646 * Get the IP address we advertise ourselves as using.
648 * Return value: The IP address. This string should not be freed.
651 gssdp_client_get_host_ip (GSSDPClient *client)
653 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
655 return client->priv->host_ip;
659 * gssdp_client_set_network:
660 * @client: A #GSSDPClient
661 * @network: The string identifying the network
663 * Sets the network identification of @client to @network.
666 gssdp_client_set_network (GSSDPClient *client,
669 g_return_if_fail (GSSDP_IS_CLIENT (client));
671 if (client->priv->network) {
672 g_free (client->priv->network);
673 client->priv->network = NULL;
677 client->priv->network = g_strdup (network);
679 g_object_notify (G_OBJECT (client), "network");
683 * gssdp_client_get_network:
684 * @client: A #GSSDPClient
686 * Get the network this client is associated with.
688 * Return value: The network identification. This string should not be freed.
691 gssdp_client_get_network (GSSDPClient *client)
693 g_return_val_if_fail (GSSDP_IS_CLIENT (client), NULL);
695 return client->priv->network;
699 * gssdp_client_get_active:
700 * @client: A #GSSDPClient
702 * Return value: %TRUE if @client is active, %FALSE otherwise.
705 gssdp_client_get_active (GSSDPClient *client)
707 g_return_val_if_fail (GSSDP_IS_CLIENT (client), FALSE);
709 return client->priv->active;
713 * _gssdp_client_send_message:
714 * @client: A #GSSDPClient
715 * @dest_ip: The destination IP address, or NULL to broadcast
716 * @dest_port: The destination port, or NULL for default
717 * @message: The message to send
719 * Sends @message to @dest_ip.
722 _gssdp_client_send_message (GSSDPClient *client,
726 _GSSDPMessageType type)
729 GError *error = NULL;
730 GInetAddress *inet_address = NULL;
731 GSocketAddress *address = NULL;
734 g_return_if_fail (GSSDP_IS_CLIENT (client));
735 g_return_if_fail (message != NULL);
737 if (!client->priv->active)
738 /* We don't send messages in passive mode */
741 /* Broadcast if @dest_ip is NULL */
745 /* Use default port if no port was explicitly specified */
747 dest_port = SSDP_PORT;
749 if (type == _GSSDP_DISCOVERY_REQUEST)
750 socket = gssdp_socket_source_get_socket
751 (client->priv->search_socket);
753 socket = gssdp_socket_source_get_socket
754 (client->priv->request_socket);
756 inet_address = g_inet_address_new_from_string (dest_ip);
757 address = g_inet_socket_address_new (inet_address, dest_port);
759 res = g_socket_send_to (socket,
767 g_warning ("Error sending SSDP packet to %s: %s",
770 g_error_free (error);
773 g_object_unref (address);
774 g_object_unref (inet_address);
778 * Generates the default server ID
781 make_server_id (void)
784 OSVERSIONINFO versioninfo;
785 versioninfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
786 if (GetVersionEx (&versioninfo)) {
787 return g_strdup_printf ("Microsoft Windows/%ld.%ld GSSDP/%s",
788 versioninfo.dwMajorVersion,
789 versioninfo.dwMinorVersion,
792 return g_strdup_printf ("Microsoft Windows GSSDP/%s",
796 struct utsname sysinfo;
800 return g_strdup_printf ("%s/%s GSSDP/%s",
808 parse_http_request (char *buf,
810 SoupMessageHeaders **headers,
813 char *req_method = NULL;
815 SoupHTTPVersion version;
817 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
819 if (soup_headers_parse_request (buf,
824 &version) == SOUP_STATUS_OK &&
825 version == SOUP_HTTP_1_1 &&
826 (path && g_ascii_strncasecmp (path, "*", 1) == 0)) {
827 if (g_ascii_strncasecmp (req_method,
829 strlen (SSDP_SEARCH_METHOD)) == 0)
830 *type = _GSSDP_DISCOVERY_REQUEST;
831 else if (g_ascii_strncasecmp (req_method,
833 strlen (GENA_NOTIFY_METHOD)) == 0)
834 *type = _GSSDP_ANNOUNCEMENT;
836 g_warning ("Unhandled method '%s'", req_method);
845 soup_message_headers_free (*headers);
859 parse_http_response (char *buf,
861 SoupMessageHeaders **headers,
866 *headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
868 if (soup_headers_parse_response (buf,
874 if (status_code == 200)
875 *type = _GSSDP_DISCOVERY_RESPONSE;
877 g_warning ("Unhandled status code '%d'", status_code);
881 soup_message_headers_free (*headers);
890 inet_netof (struct in_addr in) {
891 in_addr_t i = ntohl(in.s_addr);
894 return (((i) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT);
895 else if (IN_CLASSB (i))
896 return (((i) & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT);
898 return (((i) & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT);
903 * Called when data can be read from the socket
906 socket_source_cb (GSSDPSocketSource *socket_source, GSSDPClient *client)
909 char buf[BUF_SIZE], *end;
910 SoupMessageHeaders *headers = NULL;
912 GSocketAddress *address = NULL;
914 GInetAddress *inetaddr;
915 char *ip_string = NULL;
917 GError *error = NULL;
918 in_addr_t recv_network;
919 in_addr_t our_network;
920 struct in_addr our_addr;
921 struct sockaddr_in addr;
924 socket = gssdp_socket_source_get_socket (socket_source);
925 bytes = g_socket_receive_from (socket,
932 g_warning ("Failed to receive from socket: %s",
938 /* We need the following lines to make sure the right client received
939 * the packet. We won't need to do this if there was any way to tell
940 * Mr. Unix that we are only interested in receiving multicast packets
941 * on this socket from a particular interface but AFAIK that is not
942 * possible, at least not in a portable way.
945 if (!g_socket_address_to_native (address,
947 sizeof (struct sockaddr_in),
949 g_warning ("Could not convert address to native: %s",
955 recv_network = inet_netof (addr.sin_addr);
956 our_addr.s_addr = inet_addr (gssdp_client_get_host_ip (client));
957 our_network = inet_netof (our_addr);
958 if (recv_network != our_network)
961 if (bytes >= BUF_SIZE) {
962 g_warning ("Received packet of %u bytes, but the maximum "
963 "buffer size is %d. Packed dropped.",
964 (unsigned int) bytes, BUF_SIZE);
969 /* Add trailing \0 */
973 end = strstr (buf, "\r\n\r\n");
975 g_warning ("Received packet lacks \"\\r\\n\\r\\n\" sequence. "
987 if (!parse_http_request (buf,
991 if (!parse_http_response (buf,
995 g_warning ("Unhandled message '%s'", buf);
999 /* Emit signal if parsing succeeded */
1000 inetaddr = g_inet_socket_address_get_address (
1001 G_INET_SOCKET_ADDRESS (address));
1002 ip_string = g_inet_address_to_string (inetaddr);
1003 port = g_inet_socket_address_get_port (
1004 G_INET_SOCKET_ADDRESS (address));
1006 g_signal_emit (client,
1007 signals[MESSAGE_RECEIVED],
1017 g_error_free (error);
1023 soup_message_headers_free (headers);
1026 g_object_unref (address);
1032 request_socket_source_cb (GIOChannel *source,
1033 GIOCondition condition,
1036 GSSDPClient *client;
1038 client = GSSDP_CLIENT (user_data);
1040 return socket_source_cb (client->priv->request_socket, client);
1044 multicast_socket_source_cb (GIOChannel *source,
1045 GIOCondition condition,
1048 GSSDPClient *client;
1050 client = GSSDP_CLIENT (user_data);
1052 return socket_source_cb (client->priv->multicast_socket, client);
1056 search_socket_source_cb (GIOChannel *source,
1057 GIOCondition condition,
1060 GSSDPClient *client;
1062 client = GSSDP_CLIENT (user_data);
1064 return socket_source_cb (client->priv->search_socket, client);
1069 is_primary_adapter (PIP_ADAPTER_ADDRESSES adapter)
1072 adapter->FirstUnicastAddress->Address.lpSockaddr->sa_family;
1074 return !(adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK ||
1075 family == AF_INET6);
1079 extract_address_and_prefix (PIP_ADAPTER_UNICAST_ADDRESS adapter,
1080 PIP_ADAPTER_PREFIX prefix,
1084 DWORD len = INET6_ADDRSTRLEN;
1086 ret = WSAAddressToStringA (adapter->Address.lpSockaddr,
1087 adapter->Address.iSockaddrLength,
1095 ret = WSAAddressToStringA (prefix->Address.lpSockaddr,
1096 prefix->Address.iSockaddrLength,
1102 } else if (strcmp (iface, "127.0.0.1"))
1103 strcpy (network, "127.0.0.0");
1112 * Get the host IP for the specified interface. If no interface is specified,
1113 * it gets the IP of the first up & running interface and sets @interface
1118 get_host_ip (char **iface, char **network)
1122 GList *up_ifaces = NULL, *ifaceptr = NULL;
1123 ULONG flags = GAA_FLAG_INCLUDE_PREFIX |
1124 GAA_FLAG_SKIP_DNS_SERVER |
1125 GAA_FLAG_SKIP_MULTICAST;
1126 DWORD size = 15360; /* Use 15k buffer initially as documented in MSDN */
1128 PIP_ADAPTER_ADDRESSES adapters_addresses;
1129 PIP_ADAPTER_ADDRESSES adapter;
1132 adapters_addresses = (PIP_ADAPTER_ADDRESSES) g_malloc0 (size);
1133 ret = GetAdaptersAddresses (AF_UNSPEC,
1138 if (ret == ERROR_BUFFER_OVERFLOW)
1139 g_free (adapters_addresses);
1140 } while (ret == ERROR_BUFFER_OVERFLOW);
1142 if (ret == ERROR_SUCCESS)
1143 for (adapter = adapters_addresses;
1145 adapter = adapter->Next) {
1146 if (adapter->FirstUnicastAddress == NULL)
1148 if (adapter->OperStatus != IfOperStatusUp)
1150 /* skip Point-to-Point devices */
1151 if (adapter->IfType == IF_TYPE_PPP)
1154 if (*iface != NULL &&
1155 strcmp (*iface, adapter->AdapterName) != 0)
1158 /* I think that IPv6 is done via pseudo-adapters, so
1159 * that there are either IPv4 or IPv6 addresses defined
1161 * Loopback-Devices and IPv6 go to the end of the list,
1164 if (is_primary_adapter (adapter))
1165 up_ifaces = g_list_prepend (up_ifaces, adapter);
1167 up_ifaces = g_list_append (up_ifaces, adapter);
1170 for (ifaceptr = up_ifaces;
1172 ifaceptr = ifaceptr->next) {
1173 char ip[INET6_ADDRSTRLEN];
1174 char prefix[INET6_ADDRSTRLEN];
1176 PIP_ADAPTER_ADDRESSES adapter;
1177 PIP_ADAPTER_UNICAST_ADDRESS address;
1181 adapter = (PIP_ADAPTER_ADDRESSES) ifaceptr->data;
1182 address = adapter->FirstUnicastAddress;
1184 switch (address->Address.lpSockaddr->sa_family) {
1187 if (extract_address_and_prefix (
1189 adapter->FirstPrefix,
1201 addr = g_strdup (p);
1203 *iface = g_strdup (adapter->AdapterName);
1204 if (*network == NULL)
1205 *network = g_strdup (q);
1210 g_list_free (up_ifaces);
1211 g_free (adapters_addresses);
1215 struct ifaddrs *ifa_list, *ifa;
1217 GList *up_ifaces, *ifaceptr;
1222 if (getifaddrs (&ifa_list) != 0) {
1223 g_error ("Failed to retrieve list of network interfaces:\n%s\n",
1229 for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) {
1230 if (ifa->ifa_addr == NULL)
1233 if (*iface && strcmp (*iface, ifa->ifa_name) != 0)
1235 else if (!(ifa->ifa_flags & IFF_UP))
1237 else if ((ifa->ifa_flags & IFF_POINTOPOINT))
1240 /* Loopback and IPv6 interfaces go at the bottom on the list */
1241 if (ifa->ifa_flags & IFF_LOOPBACK ||
1242 ifa->ifa_addr->sa_family == AF_INET6)
1243 up_ifaces = g_list_append (up_ifaces, ifa);
1245 up_ifaces = g_list_prepend (up_ifaces, ifa);
1248 for (ifaceptr = up_ifaces;
1250 ifaceptr = ifaceptr->next) {
1251 char ip[INET6_ADDRSTRLEN];
1252 char net[INET6_ADDRSTRLEN];
1255 struct sockaddr_in *s4, *s4_mask;
1256 struct sockaddr_in6 *s6, *s6_mask;
1257 struct in_addr net_addr;
1258 struct in6_addr net6_addr;
1262 ifa = ifaceptr->data;
1264 switch (ifa->ifa_addr->sa_family) {
1266 s4 = (struct sockaddr_in *) ifa->ifa_addr;
1267 p = inet_ntop (AF_INET,
1268 &s4->sin_addr, ip, sizeof (ip));
1269 s4_mask = (struct sockaddr_in *) ifa->ifa_netmask;
1270 net_addr.s_addr = (in_addr_t) s4->sin_addr.s_addr &
1271 (in_addr_t) s4_mask->sin_addr.s_addr;
1272 q = inet_ntop (AF_INET, &net_addr, net, sizeof (net));
1275 s6 = (struct sockaddr_in6 *) ifa->ifa_addr;
1276 p = inet_ntop (AF_INET6,
1277 &s6->sin6_addr, ip, sizeof (ip));
1278 s6_mask = (struct sockaddr_in6 *) ifa->ifa_netmask;
1279 /* FIXME: Is this the right way to calculate this? */
1280 /* FIXME: Does this work on big endian machines? */
1281 for (i = 0; i < 16; ++i) {
1282 net6_addr.s6_addr[i] =
1283 s6->sin6_addr.s6_addr[i] &
1284 s6_mask->sin6_addr.s6_addr[i];
1286 q = inet_ntop (AF_INET6, &net6_addr, net, sizeof (net));
1289 continue; /* Unknown: ignore */
1296 *iface = g_strdup (ifa->ifa_name);
1297 if (*network == NULL)
1298 *network = g_strdup (q);
1303 g_list_free (up_ifaces);
1304 freeifaddrs (ifa_list);
1311 init_network_info (GSSDPClient *client, GError **error)
1313 gboolean ret = TRUE;
1315 if (client->priv->iface == NULL || client->priv->host_ip == NULL)
1316 client->priv->host_ip =
1317 get_host_ip (&client->priv->iface,
1318 &client->priv->network);
1320 if (client->priv->iface == NULL) {
1321 g_set_error_literal (error,
1324 "No default route?");
1327 } else if (client->priv->host_ip == NULL) {
1330 GSSDP_ERROR_NO_IP_ADDRESS,
1331 "Failed to find IP of interface %s",
1332 client->priv->iface);