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