]> code.delx.au - pulseaudio/blob - src/pulsecore/socket-server.c
use cloexec wrappers wherever applicable
[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_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38 #ifdef HAVE_SYS_UN_H
39 #include <sys/un.h>
40 #ifndef SUN_LEN
41 #define SUN_LEN(ptr) \
42 ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path))
43 #endif
44 #endif
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
47 #endif
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
50 #endif
51
52 #ifdef HAVE_LIBWRAP
53 #include <tcpd.h>
54 #endif
55
56 #ifndef HAVE_INET_NTOP
57 #include "inet_ntop.h"
58 #endif
59
60 #ifndef HAVE_INET_PTON
61 #include "inet_pton.h"
62 #endif
63
64 #include "winsock.h"
65
66 #include <pulse/xmalloc.h>
67 #include <pulse/util.h>
68
69 #include <pulsecore/socket-util.h>
70 #include <pulsecore/core-util.h>
71 #include <pulsecore/log.h>
72 #include <pulsecore/macro.h>
73 #include <pulsecore/core-error.h>
74 #include <pulsecore/refcnt.h>
75
76 #include "socket-server.h"
77
78 struct pa_socket_server {
79 PA_REFCNT_DECLARE;
80 int fd;
81 char *filename;
82 char *tcpwrap_service;
83
84 pa_socket_server_on_connection_cb_t on_connection;
85 void *userdata;
86
87 pa_io_event *io_event;
88 pa_mainloop_api *mainloop;
89 enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } 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_xnew(pa_socket_server, 1);
154 PA_REFCNT_INIT(s);
155 s->fd = fd;
156 s->filename = NULL;
157 s->on_connection = NULL;
158 s->userdata = NULL;
159 s->tcpwrap_service = NULL;
160
161 s->mainloop = m;
162 pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s));
163
164 s->type = SOCKET_SERVER_GENERIC;
165
166 return s;
167 }
168
169 pa_socket_server* pa_socket_server_ref(pa_socket_server *s) {
170 pa_assert(s);
171 pa_assert(PA_REFCNT_VALUE(s) >= 1);
172
173 PA_REFCNT_INC(s);
174 return s;
175 }
176
177 #ifdef HAVE_SYS_UN_H
178
179 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
180 int fd = -1;
181 struct sockaddr_un sa;
182 pa_socket_server *s;
183
184 pa_assert(m);
185 pa_assert(filename);
186
187 if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) {
188 pa_log("socket(): %s", pa_cstrerror(errno));
189 goto fail;
190 }
191
192 memset(&sa, 0, sizeof(sa));
193 sa.sun_family = AF_UNIX;
194 pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path));
195
196 pa_make_socket_low_delay(fd);
197
198 if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) {
199 pa_log("bind(): %s", pa_cstrerror(errno));
200 goto fail;
201 }
202
203 /* Allow access from all clients. Sockets like this one should
204 * always be put inside a directory with proper access rights,
205 * because not all OS check the access rights on the socket
206 * inodes. */
207 chmod(filename, 0777);
208
209 if (listen(fd, 5) < 0) {
210 pa_log("listen(): %s", pa_cstrerror(errno));
211 goto fail;
212 }
213
214 pa_assert_se(s = pa_socket_server_new(m, fd));
215
216 s->filename = pa_xstrdup(filename);
217 s->type = SOCKET_SERVER_UNIX;
218
219 return s;
220
221 fail:
222 if (fd >= 0)
223 pa_close(fd);
224
225 return NULL;
226 }
227
228 #else /* HAVE_SYS_UN_H */
229
230 pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) {
231 return NULL;
232 }
233
234 #endif /* HAVE_SYS_UN_H */
235
236 pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) {
237 pa_socket_server *ss;
238 int fd = -1;
239 struct sockaddr_in sa;
240 int on = 1;
241
242 pa_assert(m);
243 pa_assert(port);
244
245 if ((fd = pa_socket_cloexec(PF_INET, SOCK_STREAM, 0)) < 0) {
246 pa_log("socket(PF_INET): %s", pa_cstrerror(errno));
247 goto fail;
248 }
249
250 #ifdef SO_REUSEADDR
251 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
252 pa_log("setsockopt(): %s", pa_cstrerror(errno));
253 #endif
254
255 pa_make_tcp_socket_low_delay(fd);
256
257 memset(&sa, 0, sizeof(sa));
258 sa.sin_family = AF_INET;
259 sa.sin_port = htons(port);
260 sa.sin_addr.s_addr = htonl(address);
261
262 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
263 pa_log("bind(): %s", pa_cstrerror(errno));
264 goto fail;
265 }
266
267 if (listen(fd, 5) < 0) {
268 pa_log("listen(): %s", pa_cstrerror(errno));
269 goto fail;
270 }
271
272 if ((ss = pa_socket_server_new(m, fd))) {
273 ss->type = SOCKET_SERVER_IPV4;
274 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
275 }
276
277 return ss;
278
279 fail:
280 if (fd >= 0)
281 pa_close(fd);
282
283 return NULL;
284 }
285
286 #ifdef HAVE_IPV6
287 pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, const char *tcpwrap_service) {
288 pa_socket_server *ss;
289 int fd = -1;
290 struct sockaddr_in6 sa;
291 int on;
292
293 pa_assert(m);
294 pa_assert(port > 0);
295
296 if ((fd = pa_socket_cloexec(PF_INET6, SOCK_STREAM, 0)) < 0) {
297 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno));
298 goto fail;
299 }
300
301 #ifdef IPV6_V6ONLY
302 on = 1;
303 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
304 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno));
305 #endif
306
307 #ifdef SO_REUSEADDR
308 on = 1;
309 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
310 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno));
311 #endif
312
313 pa_make_tcp_socket_low_delay(fd);
314
315 memset(&sa, 0, sizeof(sa));
316 sa.sin6_family = AF_INET6;
317 sa.sin6_port = htons(port);
318 memcpy(sa.sin6_addr.s6_addr, address, 16);
319
320 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
321 pa_log("bind(): %s", pa_cstrerror(errno));
322 goto fail;
323 }
324
325 if (listen(fd, 5) < 0) {
326 pa_log("listen(): %s", pa_cstrerror(errno));
327 goto fail;
328 }
329
330 if ((ss = pa_socket_server_new(m, fd))) {
331 ss->type = SOCKET_SERVER_IPV6;
332 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service);
333 }
334
335 return ss;
336
337 fail:
338 if (fd >= 0)
339 pa_close(fd);
340
341 return NULL;
342 }
343 #endif
344
345 pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
346 pa_assert(m);
347 pa_assert(port > 0);
348
349 return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, tcpwrap_service);
350 }
351
352 #ifdef HAVE_IPV6
353 pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
354 pa_assert(m);
355 pa_assert(port > 0);
356
357 return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, tcpwrap_service);
358 }
359 #endif
360
361 pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
362 pa_assert(m);
363 pa_assert(port > 0);
364
365 return pa_socket_server_new_ipv4(m, INADDR_ANY, port, tcpwrap_service);
366 }
367
368 #ifdef HAVE_IPV6
369 pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, const char *tcpwrap_service) {
370 pa_assert(m);
371 pa_assert(port > 0);
372
373 return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, tcpwrap_service);
374 }
375 #endif
376
377 pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
378 struct in_addr ipv4;
379
380 pa_assert(m);
381 pa_assert(name);
382 pa_assert(port > 0);
383
384 if (inet_pton(AF_INET, name, &ipv4) > 0)
385 return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, tcpwrap_service);
386
387 return NULL;
388 }
389
390 #ifdef HAVE_IPV6
391 pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, const char *tcpwrap_service) {
392 struct in6_addr ipv6;
393
394 pa_assert(m);
395 pa_assert(name);
396 pa_assert(port > 0);
397
398 if (inet_pton(AF_INET6, name, &ipv6) > 0)
399 return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, tcpwrap_service);
400
401 return NULL;
402 }
403 #endif
404
405 static void socket_server_free(pa_socket_server*s) {
406 pa_assert(s);
407
408 if (s->filename) {
409 unlink(s->filename);
410 pa_xfree(s->filename);
411 }
412
413 pa_close(s->fd);
414
415 pa_xfree(s->tcpwrap_service);
416
417 s->mainloop->io_free(s->io_event);
418 pa_xfree(s);
419 }
420
421 void pa_socket_server_unref(pa_socket_server *s) {
422 pa_assert(s);
423 pa_assert(PA_REFCNT_VALUE(s) >= 1);
424
425 if (PA_REFCNT_DEC(s) <= 0)
426 socket_server_free(s);
427 }
428
429 void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) {
430 pa_assert(s);
431 pa_assert(PA_REFCNT_VALUE(s) >= 1);
432
433 s->on_connection = on_connection;
434 s->userdata = userdata;
435 }
436
437 char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
438 pa_assert(s);
439 pa_assert(PA_REFCNT_VALUE(s) >= 1);
440 pa_assert(c);
441 pa_assert(l > 0);
442
443 switch (s->type) {
444 #ifdef HAVE_IPV6
445 case SOCKET_SERVER_IPV6: {
446 struct sockaddr_in6 sa;
447 socklen_t sa_len = sizeof(sa);
448
449 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
450 pa_log("getsockname(): %s", pa_cstrerror(errno));
451 return NULL;
452 }
453
454 if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) {
455 char fqdn[256];
456 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
457 return NULL;
458
459 pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
460
461 } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
462 char *id;
463
464 if (!(id = pa_machine_id()))
465 return NULL;
466
467 pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port));
468 pa_xfree(id);
469 } else {
470 char ip[INET6_ADDRSTRLEN];
471
472 if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) {
473 pa_log("inet_ntop(): %s", pa_cstrerror(errno));
474 return NULL;
475 }
476
477 pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port));
478 }
479
480 return c;
481 }
482 #endif
483
484 case SOCKET_SERVER_IPV4: {
485 struct sockaddr_in sa;
486 socklen_t sa_len = sizeof(sa);
487
488 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) {
489 pa_log("getsockname(): %s", pa_cstrerror(errno));
490 return NULL;
491 }
492
493 if (sa.sin_addr.s_addr == INADDR_ANY) {
494 char fqdn[256];
495 if (!pa_get_fqdn(fqdn, sizeof(fqdn)))
496 return NULL;
497
498 pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
499 } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
500 char *id;
501
502 if (!(id = pa_machine_id()))
503 return NULL;
504
505 pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port));
506 pa_xfree(id);
507 } else {
508 char ip[INET_ADDRSTRLEN];
509
510 if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) {
511 pa_log("inet_ntop(): %s", pa_cstrerror(errno));
512 return NULL;
513 }
514
515 pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port));
516 }
517
518 return c;
519 }
520
521 case SOCKET_SERVER_UNIX: {
522 char *id;
523
524 if (!s->filename)
525 return NULL;
526
527 if (!(id = pa_machine_id()))
528 return NULL;
529
530 pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
531 pa_xfree(id);
532 return c;
533 }
534
535 default:
536 return NULL;
537 }
538 }