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