2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "socket_util.h"
26 #include "glib_compat.h"
28 #include <sys/types.h>
42 #include <netinet/in.h>
43 #include <sys/socket.h>
49 #define G_LOG_DOMAIN "listen"
51 #define DEFAULT_PORT 6600
53 struct listen_socket
{
54 struct listen_socket
*next
;
61 static struct listen_socket
*listen_sockets
;
67 return g_quark_from_static_string("listen");
71 listen_in_event(GIOChannel
*source
, GIOCondition condition
, gpointer data
);
74 listen_add_address(int pf
, const struct sockaddr
*addrp
, socklen_t addrlen
,
79 struct listen_socket
*ls
;
82 address_string
= sockaddr_to_string(addrp
, addrlen
, NULL
);
83 if (address_string
!= NULL
) {
84 g_debug("binding to socket address %s", address_string
);
85 g_free(address_string
);
88 fd
= socket_bind_listen(pf
, SOCK_STREAM
, 0, addrp
, addrlen
, 5, error
);
92 ls
= g_new(struct listen_socket
, 1);
95 channel
= g_io_channel_unix_new(fd
);
96 ls
->source_id
= g_io_add_watch(channel
, G_IO_IN
,
97 listen_in_event
, GINT_TO_POINTER(fd
));
98 g_io_channel_unref(channel
);
100 ls
->next
= listen_sockets
;
109 * Add a listener on a port on all IPv4 interfaces.
111 * @param port the TCP port
112 * @param error location to store the error occuring, or NULL to ignore errors
113 * @return true on success
116 listen_add_port_ipv4(unsigned int port
, GError
**error
)
118 struct sockaddr_in sin
;
119 const struct sockaddr
*addrp
= (const struct sockaddr
*)&sin
;
120 socklen_t addrlen
= sizeof(sin
);
122 memset(&sin
, 0, sizeof(sin
));
123 sin
.sin_port
= htons(port
);
124 sin
.sin_family
= AF_INET
;
125 sin
.sin_addr
.s_addr
= INADDR_ANY
;
127 return listen_add_address(PF_INET
, addrp
, addrlen
, error
);
132 * Add a listener on a port on all IPv6 interfaces.
134 * @param port the TCP port
135 * @param error location to store the error occuring, or NULL to ignore errors
136 * @return true on success
139 listen_add_port_ipv6(unsigned int port
, GError
**error
)
141 struct sockaddr_in6 sin
;
142 const struct sockaddr
*addrp
= (const struct sockaddr
*)&sin
;
143 socklen_t addrlen
= sizeof(sin
);
145 memset(&sin
, 0, sizeof(sin
));
146 sin
.sin6_port
= htons(port
);
147 sin
.sin6_family
= AF_INET6
;
149 return listen_add_address(PF_INET6
, addrp
, addrlen
, error
);
151 #endif /* HAVE_IPV6 */
153 #endif /* HAVE_TCP */
156 * Add a listener on a port on all interfaces.
158 * @param port the TCP port
159 * @param error location to store the error occuring, or NULL to ignore errors
160 * @return true on success
163 listen_add_port(unsigned int port
, GError
**error
)
169 GError
*error2
= NULL
;
172 g_debug("binding to any address");
175 success6
= listen_add_port_ipv6(port
, &error2
);
177 if (error2
->domain
!= listen_quark() ||
178 (error2
->code
!= EAFNOSUPPORT
&& error2
->code
!= EINVAL
&&
179 error2
->code
!= EPROTONOSUPPORT
)) {
180 g_propagate_error(error
, error2
);
184 /* although MPD was compiled with IPv6 support, this
185 host does not have it - ignore this error */
186 g_error_free(error2
);
190 success
= listen_add_port_ipv4(port
, error
);
194 /* non-critical: IPv6 listener is
196 g_clear_error(error
);
206 g_set_error(error
, listen_quark(), 0,
207 "TCP support is disabled");
209 #endif /* HAVE_TCP */
213 * Resolves a host name, and adds listeners on all addresses in the
216 * @param hostname the host name to be resolved
217 * @param port the TCP port
218 * @param error location to store the error occuring, or NULL to ignore errors
219 * @return true on success
222 listen_add_host(const char *hostname
, unsigned port
, GError
**error_r
)
225 struct addrinfo hints
, *ai
, *i
;
230 g_debug("binding to address for %s", hostname
);
232 memset(&hints
, 0, sizeof(hints
));
233 hints
.ai_flags
= AI_PASSIVE
;
235 hints
.ai_flags
|= AI_ADDRCONFIG
;
237 hints
.ai_family
= PF_UNSPEC
;
238 hints
.ai_socktype
= SOCK_STREAM
;
239 hints
.ai_protocol
= IPPROTO_TCP
;
241 g_snprintf(service
, sizeof(service
), "%u", port
);
243 ret
= getaddrinfo(hostname
, service
, &hints
, &ai
);
245 g_set_error(error_r
, listen_quark(), ret
,
246 "Failed to look up host \"%s\": %s",
247 hostname
, gai_strerror(ret
));
251 for (i
= ai
; i
!= NULL
; i
= i
->ai_next
) {
252 GError
*error
= NULL
;
254 success
= listen_add_address(i
->ai_family
, i
->ai_addr
,
255 i
->ai_addrlen
, &error
);
258 /* first bind has failed: fatal
260 g_propagate_error(error_r
, error
);
263 char *address_string
=
264 sockaddr_to_string(i
->ai_addr
,
267 if (address_string
== NULL
)
268 address_string
= g_strdup("[unknown]");
270 g_warning("bind to %s failed: %s "
271 "(continuing anyway, because at "
272 "least one address is bound)",
273 address_string
, error
->message
);
274 g_free(address_string
);
288 g_set_error(error_r
, listen_quark(), 0,
289 "TCP support is disabled");
291 #endif /* HAVE_TCP */
296 * Add a listener on a Unix domain socket.
298 * @param path the absolute socket path
299 * @param error location to store the error occuring, or NULL to ignore errors
300 * @return true on success
303 listen_add_path(const char *path
, GError
**error
)
306 struct sockaddr_un s_un
;
307 const struct sockaddr
*addrp
= (const struct sockaddr
*)&s_un
;
308 socklen_t addrlen
= sizeof(s_un
);
311 path_length
= strlen(path
);
312 if (path_length
>= sizeof(s_un
.sun_path
)) {
313 g_set_error(error
, listen_quark(), 0,
314 "unix socket path is too long");
320 s_un
.sun_family
= AF_UNIX
;
321 memcpy(s_un
.sun_path
, path
, path_length
+ 1);
323 success
= listen_add_address(PF_UNIX
, addrp
, addrlen
, error
);
327 /* allow everybody to connect */
335 listen_add_config_param(unsigned int port
,
336 const struct config_param
*param
,
339 assert(param
!= NULL
);
341 if (0 == strcmp(param
->value
, "any")) {
342 return listen_add_port(port
, error
);
344 } else if (param
->value
[0] == '/') {
345 return listen_add_path(param
->value
, error
);
348 return listen_add_host(param
->value
, port
, error
);
353 listen_global_init(GError
**error_r
)
355 int port
= config_get_positive(CONF_PORT
, DEFAULT_PORT
);
356 const struct config_param
*param
=
357 config_get_next_param(CONF_BIND_TO_ADDRESS
, NULL
);
359 GError
*error
= NULL
;
362 /* "bind_to_address" is configured, create listeners
366 success
= listen_add_config_param(port
, param
, &error
);
368 g_propagate_prefixed_error(error_r
, error
,
369 "Failed to listen on %s (line %i): ",
370 param
->value
, param
->line
);
374 param
= config_get_next_param(CONF_BIND_TO_ADDRESS
,
376 } while (param
!= NULL
);
378 /* no "bind_to_address" configured, bind the
379 configured port on all interfaces */
381 success
= listen_add_port(port
, &error
);
383 g_propagate_prefixed_error(error_r
, error
,
384 "Failed to listen on *:%d: ",
394 void listen_global_finish(void)
396 g_debug("listen_global_finish called");
398 while (listen_sockets
!= NULL
) {
399 struct listen_socket
*ls
= listen_sockets
;
400 listen_sockets
= ls
->next
;
402 g_source_remove(ls
->source_id
);
408 static int get_remote_uid(int fd
)
410 #ifdef HAVE_STRUCT_UCRED
412 socklen_t len
= sizeof (cred
);
414 if (getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &cred
, &len
) < 0)
425 listen_in_event(G_GNUC_UNUSED GIOChannel
*source
,
426 G_GNUC_UNUSED GIOCondition condition
,
429 int listen_fd
= GPOINTER_TO_INT(data
), fd
;
430 struct sockaddr_storage sa
;
431 size_t sa_length
= sizeof(sa
);
433 fd
= accept_cloexec_nonblock(listen_fd
, (struct sockaddr
*)&sa
,
436 client_new(fd
, (struct sockaddr
*)&sa
, sa_length
,
438 } else if (fd
< 0 && errno
!= EINTR
) {
439 g_warning("Problems accept()'ing");