add username to runtime directory name in /tmp/
[platform/upstream/pulseaudio.git] / polyp / 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 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 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 <sys/socket.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <sys/un.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37
38 #ifdef HAVE_LIBWRAP
39 #include <tcpd.h>
40 #endif
41
42 #include "socket-server.h"
43 #include "socket-util.h"
44 #include "xmalloc.h"
45 #include "util.h"
46 #include "log.h"
47
48 struct pa_socket_server {
49     int ref;
50     int fd;
51     char *filename;
52     char *tcpwrap_service;
53
54     void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata);
55     void *userdata;
56
57     struct pa_io_event *io_event;
58     struct pa_mainloop_api *mainloop;
59     enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type;
60 };
61
62 static void callback(struct pa_mainloop_api *mainloop, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
63     struct pa_socket_server *s = userdata;
64     struct pa_iochannel *io;
65     int nfd;
66     assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd);
67
68     pa_socket_server_ref(s);
69     
70     if ((nfd = accept(fd, NULL, NULL)) < 0) {
71         pa_log(__FILE__": accept(): %s\n", strerror(errno));
72         goto finish;
73     }
74
75     pa_fd_set_cloexec(nfd, 1);
76     
77     if (!s->on_connection) {
78         close(nfd);
79         goto finish;
80     }
81
82 #ifdef HAVE_LIBWRAP
83
84     if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) {
85         struct request_info req;
86
87         request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL);
88         fromhost(&req);
89         if (!hosts_access(&req)) {
90             pa_log(__FILE__": TCP connection refused by tcpwrap.\n");
91             close(nfd);
92             goto finish;
93         }
94
95         pa_log(__FILE__": TCP connection accepted by tcpwrap.\n");
96     }
97 #endif
98     
99     /* There should be a check for socket type here */
100     if (s->type == SOCKET_SERVER_IPV4) 
101         pa_socket_tcp_low_delay(fd);
102     else
103         pa_socket_low_delay(fd);
104     
105     io = pa_iochannel_new(s->mainloop, nfd, nfd);
106     assert(io);
107     s->on_connection(s, io, s->userdata);
108
109 finish:
110     pa_socket_server_unref(s);
111 }
112
113 struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd) {
114     struct pa_socket_server *s;
115     assert(m && fd >= 0);
116     
117     s = pa_xmalloc(sizeof(struct pa_socket_server));
118     s->ref = 1;
119     s->fd = fd;
120     s->filename = NULL;
121     s->on_connection = NULL;
122     s->userdata = NULL;
123     s->tcpwrap_service = NULL;
124
125     s->mainloop = m;
126     s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s);
127     assert(s->io_event);
128
129     s->type = SOCKET_SERVER_GENERIC;
130     
131     return s;
132 }
133
134 struct pa_socket_server* pa_socket_server_ref(struct pa_socket_server *s) {
135     assert(s && s->ref >= 1);
136     s->ref++;
137     return s;
138 }
139
140 struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename) {
141     int fd = -1;
142     struct sockaddr_un sa;
143     struct pa_socket_server *s;
144     
145     assert(m && filename);
146
147     if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
148         pa_log(__FILE__": socket(): %s\n", strerror(errno));
149         goto fail;
150     }
151
152     pa_fd_set_cloexec(fd, 1);
153
154     sa.sun_family = AF_LOCAL;
155     strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
156     sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
157
158     pa_socket_low_delay(fd);
159     
160     if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) {
161         pa_log(__FILE__": bind(): %s\n", strerror(errno));
162         goto fail;
163     }
164
165     if (listen(fd, 5) < 0) {
166         pa_log(__FILE__": listen(): %s\n", strerror(errno));
167         goto fail;
168     }
169
170     s = pa_socket_server_new(m, fd);
171     assert(s);
172
173     s->filename = pa_xstrdup(filename);
174     s->type = SOCKET_SERVER_UNIX;
175     
176     return s;
177                                                                                                                                                                          
178 fail:
179     if (fd >= 0)
180         close(fd);
181
182     return NULL;
183 }
184
185 struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
186     struct pa_socket_server *ss;
187     int fd = -1;
188     struct sockaddr_in sa;
189     int on = 1;
190
191     assert(m && port);
192
193     if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
194         pa_log(__FILE__": socket(): %s\n", strerror(errno));
195         goto fail;
196     }
197
198     pa_fd_set_cloexec(fd, 1);
199
200     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
201         pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
202
203     pa_socket_tcp_low_delay(fd);
204     
205     memset(&sa, sizeof(sa), 0);
206     sa.sin_family = AF_INET;
207     sa.sin_port = htons(port);
208     sa.sin_addr.s_addr = htonl(address);
209
210     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
211         pa_log(__FILE__": bind(): %s\n", strerror(errno));
212         goto fail;
213     }
214
215     if (listen(fd, 5) < 0) {
216         pa_log(__FILE__": listen(): %s\n", strerror(errno));
217         goto fail;
218     }
219
220     if ((ss = pa_socket_server_new(m, fd))) {
221         ss->type = SOCKET_SERVER_IPV4;
222         ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
223     }
224
225     return ss;
226     
227 fail:
228     if (fd >= 0)
229         close(fd);
230
231     return NULL;
232 }
233
234 struct pa_socket_server* pa_socket_server_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
235     struct pa_socket_server *ss;
236     int fd = -1;
237     struct sockaddr_in6 sa;
238     int on = 1;
239
240     assert(m && port);
241
242     if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
243         pa_log(__FILE__": socket(): %s\n", strerror(errno));
244         goto fail;
245     }
246
247     pa_fd_set_cloexec(fd, 1);
248
249     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
250         pa_log(__FILE__": setsockopt(): %s\n", strerror(errno));
251
252     pa_socket_tcp_low_delay(fd);
253
254     memset(&sa, sizeof(sa), 0);
255     sa.sin6_family = AF_INET6;
256     sa.sin6_port = htons(port);
257     memcpy(sa.sin6_addr.s6_addr, address, 16);
258
259     if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
260         pa_log(__FILE__": bind(): %s\n", strerror(errno));
261         goto fail;
262     }
263
264     if (listen(fd, 5) < 0) {
265         pa_log(__FILE__": listen(): %s\n", strerror(errno));
266         goto fail;
267     }
268
269     if ((ss = pa_socket_server_new(m, fd)))
270         ss->type = SOCKET_SERVER_IPV6;
271
272     return ss;
273     
274 fail:
275     if (fd >= 0)
276         close(fd);
277
278     return NULL;
279 }
280
281 static void socket_server_free(struct pa_socket_server*s) {
282     assert(s);
283     close(s->fd);
284
285     if (s->filename) {
286         unlink(s->filename);
287         pa_xfree(s->filename);
288     }
289
290     pa_xfree(s->tcpwrap_service);
291
292     s->mainloop->io_free(s->io_event);
293     pa_xfree(s);
294 }
295
296 void pa_socket_server_unref(struct pa_socket_server *s) {
297     assert(s && s->ref >= 1);
298
299     if (!(--(s->ref)))
300         socket_server_free(s);
301 }
302
303 void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata) {
304     assert(s && s->ref >= 1);
305
306     s->on_connection = on_connection;
307     s->userdata = userdata;
308 }
309
310
311 char *pa_socket_server_get_address(struct pa_socket_server *s, char *c, size_t l) {
312     assert(s && c && l > 0);
313     
314     switch (s->type) {
315         case SOCKET_SERVER_IPV6: {
316             struct sockaddr_in6 sa;
317             socklen_t l = sizeof(sa);
318
319             if (getsockname(s->fd, (struct sockaddr*) &sa, &l) < 0) {
320                 pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
321                 return NULL;
322             }
323
324             if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
325                 char fqdn[256];
326                 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
327                     return NULL;
328                 
329                 snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
330                 
331             } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
332                 char hn[256];
333                 if (!pa_get_host_name(hn, sizeof(hn)))
334                     return NULL;
335                 
336                 snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
337             } else {
338                 char ip[INET6_ADDRSTRLEN];
339                 
340                 if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
341                     pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
342                     return NULL;
343                 }
344                 
345                 snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
346             }
347
348             return c;
349         }
350
351         case SOCKET_SERVER_IPV4: {
352             struct sockaddr_in sa;
353             socklen_t l = sizeof(sa);
354
355             if (getsockname(s->fd, &sa, &l) < 0) {
356                 pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno));
357                 return NULL;
358             }
359
360             if (sa.sin_addr.s_addr == INADDR_ANY) {
361                 char fqdn[256];
362                 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
363                     return NULL;
364                 
365                 snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
366             } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
367                 char hn[256];
368                 if (!pa_get_host_name(hn, sizeof(hn)))
369                     return NULL;
370                 
371                 snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
372             } else {
373                 char ip[INET_ADDRSTRLEN];
374
375                 if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
376                     pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno));
377                     return NULL;
378                 }
379                 
380                 snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
381
382             }
383             
384             return c;
385         }
386
387         case SOCKET_SERVER_UNIX: {
388             char hn[256];
389
390             if (!s->filename)
391                 return NULL;
392             
393             if (!pa_get_host_name(hn, sizeof(hn)))
394                 return NULL;
395
396             snprintf(c, l, "{%s}unix:%s", hn, s->filename);
397             return c;
398         }
399
400         default:
401             return NULL;
402     }
403 }