d1646984c0af5436aa1e980d1579396b98a1f3e8
[profile/ivi/pulseaudio.git] / src / polypcore / socket-server.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
6   polypaudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   or (at your option) any later version.
10  
11   polypaudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public License
17   along with polypaudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <unistd.h>
33
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
36 #endif
37 #ifdef HAVE_SYS_UN_H
38 #include <sys/un.h>
39 #ifndef SUN_LEN
40 #define SUN_LEN(ptr) \
41     ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
42 #endif
43 #endif
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
46 #endif
47 #ifdef HAVE_NETINET_IN_H
48 #include <netinet/in.h>
49 #endif
50
51 #ifdef HAVE_LIBWRAP
52 #include <tcpd.h>
53 #endif
54
55 #ifndef HAVE_INET_NTOP
56 #include "inet_ntop.h"
57 #endif
58
59 #include "winsock.h"
60
61 #include <polypcore/socket-util.h>
62 #include <polypcore/xmalloc.h>
63 #include <polypcore/util.h>
64 #include <polypcore/log.h>
65
66 #include "socket-server.h"
67
68 struct pa_socket_server {
69     int ref;
70     int fd;
71     char *filename;
72     char *tcpwrap_service;
73
74     void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata);
75     void *userdata;
76
77     pa_io_event *io_event;
78     pa_mainloop_api *mainloop;
79     enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
80 };
81
82 static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) {
83     pa_socket_server *s = userdata;
84     pa_iochannel *io;
85     int nfd;
86     assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
87
88     pa_socket_server_ref(s);
89     
90     if ((nfd = accept(fd, NULL, NULL)) < 0) {
91         pa_log(__FILE__": accept(): %s\n", strerror(errno));
92         goto finish;
93     }
94
95     pa_fd_set_cloexec(nfd, 1);
96     
97     if (!s->on_connection) {
98         close(nfd);
99         goto finish;
100     }
101
102 #ifdef HAVE_LIBWRAP
103
104     if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) {
105         struct request_info req;
106
107         request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
108         fromhost(&req);
109         if (!hosts_access(&req)) {
110             pa_log(__FILE__": TCP connection refused by tcpwrap.\n");
111             close(nfd);
112             goto finish;
113         }
114
115         pa_log(__FILE__": TCP connection accepted by tcpwrap.\n");
116     }
117 #endif
118     
119     /* There should be a check for socket type here */
120     if (s->type == SOCKET_SERVER_IPV4) 
121         pa_socket_tcp_low_delay(fd);
122     else
123         pa_socket_low_delay(fd);
124     
125     io = pa_iochannel_new(s->mainloop, nfd, nfd);
126     assert(io);
127     s->on_connection(s, io, s->userdata);
128
129 finish:
130     pa_socket_server_unref(s);
131 }
132
133 pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) {
134     pa_socket_server *s;
135     assert(m && fd >= 0);
136     
137     s = pa_xmalloc(sizeof(pa_socket_server));
138     s->ref = 1;
139     s->fd = fd;
140     s->filename = NULL;
141     s->on_connection = NULL;
142     s->userdata = NULL;
143     s->tcpwrap_service = NULL;
144
145     s->mainloop = m;
146     s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
147     assert(s->io_event);
148
149     s->type = SOCKET_SERVER_GENERIC;
150     
151     return s;
152 }
153
154 pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
155     assert(s && s->ref >= 1);
156     s->ref++;
157     return s;
158 }
159
160 #ifdef HAVE_SYS_UN_H
161
162 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
163     int fd = -1;
164     struct sockaddr_un sa;
165     pa_socket_server *s;
166     
167     assert(m && filename);
168
169     if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
170         pa_log(__FILE__": socket(): %s\n", strerror(errno));
171         goto fail;
172     }
173
174     pa_fd_set_cloexec(fd, 1);
175
176     sa.sun_family = AF_UNIX;
177     strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
178     sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
179
180     pa_socket_low_delay(fd);
181     
182     if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
183         pa_log(__FILE__": bind(): %s\n", strerror(errno));
184         goto fail;
185     }
186
187     if (listen(fd, 5) < 0) {
188         pa_log(__FILE__": listen(): %s\n", strerror(errno));
189         goto fail;
190     }
191
192     s = pa_socket_server_new(m, fd);
193     assert(s);
194
195     s->filename = pa_xstrdup(filename);
196     s->type = SOCKET_SERVER_UNIX;
197     
198     return s;
199                                                                                                                                                                          
200 fail:
201     if (fd >= 0)
202         close(fd);
203
204     return NULL;
205 }
206
207 #else /* HAVE_SYS_UN_H */
208
209 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
210     return NULL;
211 }
212
213 #endif /* HAVE_SYS_UN_H */
214
215 pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
216     pa_socket_server *ss;
217     int fd = -1;
218     struct sockaddr_in sa;
219     int on = 1;
220
221     assert(m && port);
222
223     if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
224         pa_log(__FILE__": socket(): %s\n", strerror(errno));
225         goto fail;
226     }
227
228     pa_fd_set_cloexec(fd, 1);
229
230     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
231         pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
232
233     pa_socket_tcp_low_delay(fd);
234     
235     memset(&sa, 0, sizeof(sa));
236     sa.sin_family = AF_INET;
237     sa.sin_port = htons(port);
238     sa.sin_addr.s_addr = htonl(address);
239
240     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
241         pa_log(__FILE__": bind(): %s\n", strerror(errno));
242         goto fail;
243     }
244
245     if (listen(fd, 5) < 0) {
246         pa_log(__FILE__": listen(): %s\n", strerror(errno));
247         goto fail;
248     }
249
250     if ((ss = pa_socket_server_new(m, fd))) {
251         ss->type = SOCKET_SERVER_IPV4;
252         ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
253     }
254
255     return ss;
256     
257 fail:
258     if (fd >= 0)
259         close(fd);
260
261     return NULL;
262 }
263
264 pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port) {
265     pa_socket_server *ss;
266     int fd = -1;
267     struct sockaddr_in6 sa;
268     int on = 1;
269
270     assert(m && port);
271
272     if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
273         pa_log(__FILE__": socket(): %s\n", strerror(errno));
274         goto fail;
275     }
276
277     pa_fd_set_cloexec(fd, 1);
278
279     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
280         pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
281
282     pa_socket_tcp_low_delay(fd);
283
284     memset(&sa, 0, sizeof(sa));
285     sa.sin6_family = AF_INET6;
286     sa.sin6_port = htons(port);
287     memcpy(sa.sin6_addr.s6_addr, address, 16);
288
289     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
290         pa_log(__FILE__": bind(): %s\n", strerror(errno));
291         goto fail;
292     }
293
294     if (listen(fd, 5) < 0) {
295         pa_log(__FILE__": listen(): %s\n", strerror(errno));
296         goto fail;
297     }
298
299     if ((ss = pa_socket_server_new(m, fd)))
300         ss->type = SOCKET_SERVER_IPV6;
301
302     return ss;
303     
304 fail:
305     if (fd >= 0)
306         close(fd);
307
308     return NULL;
309 }
310
311 static void socket_server_free(pa_socket_server*s) {
312     assert(s);
313     close(s->fd);
314
315     if (s->filename) {
316         unlink(s->filename);
317         pa_xfree(s->filename);
318     }
319
320     pa_xfree(s->tcpwrap_service);
321
322     s->mainloop->io_free(s->io_event);
323     pa_xfree(s);
324 }
325
326 void pa_socket_server_unref(pa_socket_server *s) {
327     assert(s && s->ref >= 1);
328
329     if (!(--(s->ref)))
330         socket_server_free(s);
331 }
332
333 void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) {
334     assert(s && s->ref >= 1);
335
336     s->on_connection = on_connection;
337     s->userdata = userdata;
338 }
339
340
341 char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
342     assert(s && c && l > 0);
343     
344     switch (s->type) {
345         case SOCKET_SERVER_IPV6: {
346             struct sockaddr_in6 sa;
347             socklen_t sa_len = sizeof(sa);
348
349             if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
350                 pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
351                 return NULL;
352             }
353
354             if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
355                 char fqdn[256];
356                 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
357                     return NULL;
358                 
359                 snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
360                 
361             } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
362                 char hn[256];
363                 if (!pa_get_host_name(hn, sizeof(hn)))
364                     return NULL;
365                 
366                 snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
367             } else {
368                 char ip[INET6_ADDRSTRLEN];
369                 
370                 if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
371                     pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
372                     return NULL;
373                 }
374                 
375                 snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
376             }
377
378             return c;
379         }
380
381         case SOCKET_SERVER_IPV4: {
382             struct sockaddr_in sa;
383             socklen_t sa_len = sizeof(sa);
384
385             if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
386                 pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
387                 return NULL;
388             }
389
390             if (sa.sin_addr.s_addr == INADDR_ANY) {
391                 char fqdn[256];
392                 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
393                     return NULL;
394                 
395                 snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
396             } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
397                 char hn[256];
398                 if (!pa_get_host_name(hn, sizeof(hn)))
399                     return NULL;
400                 
401                 snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
402             } else {
403                 char ip[INET_ADDRSTRLEN];
404
405                 if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
406                     pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
407                     return NULL;
408                 }
409                 
410                 snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
411
412             }
413             
414             return c;
415         }
416
417         case SOCKET_SERVER_UNIX: {
418             char hn[256];
419
420             if (!s->filename)
421                 return NULL;
422             
423             if (!pa_get_host_name(hn, sizeof(hn)))
424                 return NULL;
425
426             snprintf(c, l, "{%s}unix:%s", hn, s->filename);
427             return c;
428         }
429
430         default:
431             return NULL;
432     }
433 }