Fix the lib path
[external/gssdp.git] / libgssdp / gssdp-socket-source.c
1 /* 
2  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
3  * Copyright (C) 2009 Nokia Corporation, all rights reserved.
4  *
5  * Author: Jorn Baayen <jorn@openedhand.com>
6  *         Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
7  *                               <zeeshan.ali@nokia.com>
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include <config.h>
26 #include <glib.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <unistd.h>
35
36 #include "gssdp-socket-source.h"
37 #include "gssdp-protocol.h"
38
39 struct _GSSDPSocketSource {
40         GSource source;
41
42         GPollFD poll_fd;
43 };
44
45 static gboolean
46 gssdp_socket_source_prepare  (GSource    *source,
47                               int        *timeout);
48 static gboolean
49 gssdp_socket_source_check    (GSource    *source);
50 static gboolean
51 gssdp_socket_source_dispatch (GSource    *source,
52                               GSourceFunc callback,
53                               gpointer    user_data);
54 static void
55 gssdp_socket_source_finalize (GSource    *source);
56
57 static const GSourceFuncs gssdp_socket_source_funcs = {
58         gssdp_socket_source_prepare,
59         gssdp_socket_source_check,
60         gssdp_socket_source_dispatch,
61         gssdp_socket_source_finalize
62 };
63
64 /**
65  * gssdp_socket_source_new
66  *
67  * Return value: A new #GSSDPSocketSource
68  **/
69 GSSDPSocketSource *
70 gssdp_socket_source_new (GSSDPSocketSourceType type,
71                          const char           *host_ip)
72 {
73         GSource *source;
74         GSSDPSocketSource *socket_source;
75         struct sockaddr_in bind_addr;
76         struct in_addr iface_addr;
77         struct ip_mreq mreq;
78         gboolean boolean = TRUE;
79         guchar ttl = 4;
80         int res;
81
82         /* Create source */
83         source = g_source_new ((GSourceFuncs*)&gssdp_socket_source_funcs,
84                                sizeof (GSSDPSocketSource));
85
86         socket_source = (GSSDPSocketSource *) source;
87
88         /* Create socket */
89         socket_source->poll_fd.fd = socket (AF_INET,
90                                             SOCK_DGRAM,
91                                             IPPROTO_UDP);
92         if (socket_source->poll_fd.fd == -1)
93                 goto error;
94         
95         socket_source->poll_fd.events = G_IO_IN | G_IO_ERR;
96
97         g_source_add_poll (source, &socket_source->poll_fd);
98
99         /* Enable broadcasting */
100         res = setsockopt (socket_source->poll_fd.fd, 
101                           SOL_SOCKET,
102                           SO_BROADCAST,
103                           &boolean,
104                           sizeof (boolean));
105         if (res == -1)
106                 goto error;
107
108         /* TTL */
109         res = setsockopt (socket_source->poll_fd.fd,
110                           IPPROTO_IP,
111                           IP_MULTICAST_TTL,
112                           &ttl,
113                           sizeof (ttl));
114         if (res == -1)
115                 goto error;
116
117         memset (&bind_addr, 0, sizeof (bind_addr));
118         bind_addr.sin_family = AF_INET;
119
120         res = inet_aton (host_ip, &iface_addr);
121         if (res == 0)
122                 goto error;
123
124         /* Set up additional things according to the type of socket desired */
125         if (type == GSSDP_SOCKET_SOURCE_TYPE_MULTICAST) {
126                 /* Allow multiple sockets to use the same PORT number */
127                 res = setsockopt (socket_source->poll_fd.fd,
128                                   SOL_SOCKET,
129 #ifdef SO_REUSEPORT 
130                                   SO_REUSEPORT,
131 #else
132                                   SO_REUSEADDR,
133 #endif
134                                   &boolean,
135                                   sizeof (boolean));
136                 if (res == -1)
137                         goto error;
138
139                 /* Enable multicast loopback */
140                 res = setsockopt (socket_source->poll_fd.fd,
141                                   IPPROTO_IP,
142                                   IP_MULTICAST_LOOP,
143                                   &boolean,
144                                   sizeof (boolean));
145                 if (res == -1)
146                        goto error;
147
148                 /* Set the interface */
149                 res = setsockopt (socket_source->poll_fd.fd,
150                                   IPPROTO_IP,
151                                   IP_MULTICAST_IF,
152                                   &iface_addr,
153                                   sizeof (struct in_addr));
154                 if (res == -1)
155                         goto error;
156
157                 /* Subscribe to multicast channel */
158                 res = inet_aton (SSDP_ADDR, &(mreq.imr_multiaddr));
159                 if (res == 0)
160                         goto error;
161
162                 memcpy (&(mreq.imr_interface),
163                         &iface_addr,
164                         sizeof (struct in_addr));
165
166                 res = setsockopt (socket_source->poll_fd.fd,
167                                   IPPROTO_IP,
168                                   IP_ADD_MEMBERSHIP,
169                                   &mreq,
170                                   sizeof (mreq));
171                 if (res == -1)
172                         goto error;
173
174                 bind_addr.sin_port = htons (SSDP_PORT);
175                 res = inet_aton (SSDP_ADDR, &(bind_addr.sin_addr));
176                 if (res == 0)
177                         goto error;
178         } else {
179                 bind_addr.sin_port = 0;
180                 memcpy (&(bind_addr.sin_addr),
181                         &iface_addr,
182                         sizeof (struct in_addr));
183         }
184
185         /* Bind to requested port and address */
186         res = bind (socket_source->poll_fd.fd,
187                     (struct sockaddr *) &bind_addr,
188                     sizeof (bind_addr));
189         if (res == -1)
190                 goto error;
191
192         return socket_source;
193
194 error:
195         g_source_destroy (source);
196         
197         return NULL;
198 }
199
200 static gboolean
201 gssdp_socket_source_prepare (GSource *source,
202                              int     *timeout)
203 {
204         return FALSE;
205 }
206
207 static gboolean
208 gssdp_socket_source_check (GSource *source)
209 {
210         GSSDPSocketSource *socket_source;
211
212         socket_source = (GSSDPSocketSource *) source;
213
214         return socket_source->poll_fd.revents & (G_IO_IN | G_IO_ERR);
215 }
216
217 static gboolean
218 gssdp_socket_source_dispatch (GSource    *source,
219                               GSourceFunc callback,
220                               gpointer    user_data)
221 {
222         GSSDPSocketSource *socket_source;
223
224         socket_source = (GSSDPSocketSource *) source;
225
226         if (socket_source->poll_fd.revents & G_IO_IN) {
227                 /* Ready to read */
228                 if (callback)
229                         callback (user_data);
230         } else if (socket_source->poll_fd.revents & G_IO_ERR) {
231                 /* Error */
232                 int value;
233                 socklen_t size_int;
234
235                 value = EINVAL;
236                 size_int = sizeof (int);
237                 
238                 /* Get errno from socket */
239                 getsockopt (socket_source->poll_fd.fd,
240                             SOL_SOCKET,
241                             SO_ERROR,
242                             &value,
243                             &size_int);
244
245                 g_warning ("Socket error %d received: %s",
246                            value,
247                            strerror (value));
248         }
249
250         return TRUE;
251 }
252
253 static void
254 gssdp_socket_source_finalize (GSource *source)
255 {
256         GSSDPSocketSource *socket_source;
257
258         socket_source = (GSSDPSocketSource *) source;
259         
260         /* Close the socket */
261         close (socket_source->poll_fd.fd);
262 }
263
264 /**
265  * gssdp_socket_source_get_fd
266  *
267  * Return value: The socket's FD.
268  **/
269 int
270 gssdp_socket_source_get_fd (GSSDPSocketSource *socket_source)
271 {
272         g_return_val_if_fail (socket_source != NULL, -1);
273         
274         return socket_source->poll_fd.fd;
275 }