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