Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
18e3d0b5
Commit
18e3d0b5
authored
Oct 05, 2010
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
listen: move generic code to server_socket.c
parent
04c4398b
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
553 additions
and
354 deletions
+553
-354
Makefile.am
Makefile.am
+2
-0
listen.c
src/listen.c
+20
-354
server_socket.c
src/server_socket.c
+447
-0
server_socket.h
src/server_socket.h
+84
-0
No files found.
Makefile.am
View file @
18e3d0b5
...
@@ -111,6 +111,7 @@ mpd_headers = \
...
@@ -111,6 +111,7 @@ mpd_headers = \
src/icy_metadata.h
\
src/icy_metadata.h
\
src/client.h
\
src/client.h
\
src/client_internal.h
\
src/client_internal.h
\
src/server_socket.h
\
src/listen.h
\
src/listen.h
\
src/log.h
\
src/log.h
\
src/ls.h
\
src/ls.h
\
...
@@ -275,6 +276,7 @@ src_mpd_SOURCES = \
...
@@ -275,6 +276,7 @@ src_mpd_SOURCES = \
src/client_process.c
\
src/client_process.c
\
src/client_read.c
\
src/client_read.c
\
src/client_write.c
\
src/client_write.c
\
src/server_socket.c
\
src/listen.c
\
src/listen.c
\
src/log.c
\
src/log.c
\
src/ls.c
\
src/ls.c
\
...
...
src/listen.c
View file @
18e3d0b5
...
@@ -19,333 +19,44 @@
...
@@ -19,333 +19,44 @@
#include "config.h"
#include "config.h"
#include "listen.h"
#include "listen.h"
#include "s
ocket_util
.h"
#include "s
erver_socket
.h"
#include "client.h"
#include "client.h"
#include "conf.h"
#include "conf.h"
#include "fd_util.h"
#include "glib_compat.h"
#include "glib_compat.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <assert.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#endif
#undef G_LOG_DOMAIN
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "listen"
#define G_LOG_DOMAIN "listen"
#define DEFAULT_PORT 6600
#define DEFAULT_PORT 6600
struct
listen_socket
{
static
struct
server_socket
*
listen_socket
;
struct
listen_socket
*
next
;
int
fd
;
guint
source_id
;
};
static
struct
listen_socket
*
listen_sockets
;
int
listen_port
;
int
listen_port
;
static
GQuark
static
void
listen_quark
(
void
)
listen_callback
(
int
fd
,
const
struct
sockaddr
*
address
,
{
size_t
address_length
,
int
uid
,
G_GNUC_UNUSED
void
*
ctx
)
return
g_quark_from_static_string
(
"listen"
);
}
static
gboolean
listen_in_event
(
GIOChannel
*
source
,
GIOCondition
condition
,
gpointer
data
);
static
bool
listen_add_address
(
int
pf
,
const
struct
sockaddr
*
addrp
,
socklen_t
addrlen
,
GError
**
error
)
{
char
*
address_string
;
int
fd
;
struct
listen_socket
*
ls
;
GIOChannel
*
channel
;
address_string
=
sockaddr_to_string
(
addrp
,
addrlen
,
NULL
);
if
(
address_string
!=
NULL
)
{
g_debug
(
"binding to socket address %s"
,
address_string
);
g_free
(
address_string
);
}
fd
=
socket_bind_listen
(
pf
,
SOCK_STREAM
,
0
,
addrp
,
addrlen
,
5
,
error
);
if
(
fd
<
0
)
return
false
;
ls
=
g_new
(
struct
listen_socket
,
1
);
ls
->
fd
=
fd
;
channel
=
g_io_channel_unix_new
(
fd
);
ls
->
source_id
=
g_io_add_watch
(
channel
,
G_IO_IN
,
listen_in_event
,
GINT_TO_POINTER
(
fd
));
g_io_channel_unref
(
channel
);
ls
->
next
=
listen_sockets
;
listen_sockets
=
ls
;
return
true
;
}
#ifdef HAVE_TCP
/**
* Add a listener on a port on all IPv4 interfaces.
*
* @param port the TCP port
* @param error location to store the error occuring, or NULL to ignore errors
* @return true on success
*/
static
bool
listen_add_port_ipv4
(
unsigned
int
port
,
GError
**
error
)
{
struct
sockaddr_in
sin
;
const
struct
sockaddr
*
addrp
=
(
const
struct
sockaddr
*
)
&
sin
;
socklen_t
addrlen
=
sizeof
(
sin
);
memset
(
&
sin
,
0
,
sizeof
(
sin
));
sin
.
sin_port
=
htons
(
port
);
sin
.
sin_family
=
AF_INET
;
sin
.
sin_addr
.
s_addr
=
INADDR_ANY
;
return
listen_add_address
(
PF_INET
,
addrp
,
addrlen
,
error
);
}
#ifdef HAVE_IPV6
/**
* Add a listener on a port on all IPv6 interfaces.
*
* @param port the TCP port
* @param error location to store the error occuring, or NULL to ignore errors
* @return true on success
*/
static
bool
listen_add_port_ipv6
(
unsigned
int
port
,
GError
**
error
)
{
{
struct
sockaddr_in6
sin
;
client_new
(
fd
,
address
,
address_length
,
uid
);
const
struct
sockaddr
*
addrp
=
(
const
struct
sockaddr
*
)
&
sin
;
socklen_t
addrlen
=
sizeof
(
sin
);
memset
(
&
sin
,
0
,
sizeof
(
sin
));
sin
.
sin6_port
=
htons
(
port
);
sin
.
sin6_family
=
AF_INET6
;
return
listen_add_address
(
PF_INET6
,
addrp
,
addrlen
,
error
);
}
}
#endif
/* HAVE_IPV6 */
#endif
/* HAVE_TCP */
/**
* Add a listener on a port on all interfaces.
*
* @param port the TCP port
* @param error location to store the error occuring, or NULL to ignore errors
* @return true on success
*/
static
bool
listen_add_port
(
unsigned
int
port
,
GError
**
error
)
{
#ifdef HAVE_TCP
bool
success
;
#ifdef HAVE_IPV6
bool
success6
;
GError
*
error2
=
NULL
;
#endif
g_debug
(
"binding to any address"
);
#ifdef HAVE_IPV6
success6
=
listen_add_port_ipv6
(
port
,
&
error2
);
if
(
!
success6
)
{
if
(
error2
->
domain
!=
listen_quark
()
||
(
error2
->
code
!=
EAFNOSUPPORT
&&
error2
->
code
!=
EINVAL
&&
error2
->
code
!=
EPROTONOSUPPORT
))
{
g_propagate_error
(
error
,
error2
);
return
false
;
}
/* although MPD was compiled with IPv6 support, this
host does not have it - ignore this error */
g_error_free
(
error2
);
}
#endif
success
=
listen_add_port_ipv4
(
port
,
error
);
if
(
!
success
)
{
#ifdef HAVE_IPV6
if
(
success6
)
/* non-critical: IPv6 listener is
already set up */
g_clear_error
(
error
);
else
#endif
return
false
;
}
return
true
;
#else
/* HAVE_TCP */
(
void
)
port
;
g_set_error
(
error
,
listen_quark
(),
0
,
"TCP support is disabled"
);
return
false
;
#endif
/* HAVE_TCP */
}
/**
* Resolves a host name, and adds listeners on all addresses in the
* result set.
*
* @param hostname the host name to be resolved
* @param port the TCP port
* @param error location to store the error occuring, or NULL to ignore errors
* @return true on success
*/
static
bool
listen_add_host
(
const
char
*
hostname
,
unsigned
port
,
GError
**
error_r
)
{
#ifdef HAVE_TCP
struct
addrinfo
hints
,
*
ai
,
*
i
;
char
service
[
20
];
int
ret
;
bool
success
;
g_debug
(
"binding to address for %s"
,
hostname
);
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_flags
=
AI_PASSIVE
;
#ifdef AI_ADDRCONFIG
hints
.
ai_flags
|=
AI_ADDRCONFIG
;
#endif
hints
.
ai_family
=
PF_UNSPEC
;
hints
.
ai_socktype
=
SOCK_STREAM
;
hints
.
ai_protocol
=
IPPROTO_TCP
;
g_snprintf
(
service
,
sizeof
(
service
),
"%u"
,
port
);
ret
=
getaddrinfo
(
hostname
,
service
,
&
hints
,
&
ai
);
if
(
ret
!=
0
)
{
g_set_error
(
error_r
,
listen_quark
(),
ret
,
"Failed to look up host
\"
%s
\"
: %s"
,
hostname
,
gai_strerror
(
ret
));
return
false
;
}
for
(
i
=
ai
;
i
!=
NULL
;
i
=
i
->
ai_next
)
{
GError
*
error
=
NULL
;
success
=
listen_add_address
(
i
->
ai_family
,
i
->
ai_addr
,
i
->
ai_addrlen
,
&
error
);
if
(
!
success
)
{
if
(
i
==
ai
)
{
/* first bind has failed: fatal
error */
g_propagate_error
(
error_r
,
error
);
return
false
;
}
else
{
char
*
address_string
=
sockaddr_to_string
(
i
->
ai_addr
,
i
->
ai_addrlen
,
NULL
);
if
(
address_string
==
NULL
)
address_string
=
g_strdup
(
"[unknown]"
);
g_warning
(
"bind to %s failed: %s "
"(continuing anyway, because at "
"least one address is bound)"
,
address_string
,
error
->
message
);
g_free
(
address_string
);
g_error_free
(
error
);
}
}
}
freeaddrinfo
(
ai
);
return
true
;
#else
/* HAVE_TCP */
(
void
)
hostname
;
(
void
)
port
;
g_set_error
(
error_r
,
listen_quark
(),
0
,
"TCP support is disabled"
);
return
false
;
#endif
/* HAVE_TCP */
}
#ifdef HAVE_UN
/**
* Add a listener on a Unix domain socket.
*
* @param path the absolute socket path
* @param error location to store the error occuring, or NULL to ignore errors
* @return true on success
*/
static
bool
listen_add_path
(
const
char
*
path
,
GError
**
error
)
{
size_t
path_length
;
struct
sockaddr_un
s_un
;
const
struct
sockaddr
*
addrp
=
(
const
struct
sockaddr
*
)
&
s_un
;
socklen_t
addrlen
=
sizeof
(
s_un
);
bool
success
;
path_length
=
strlen
(
path
);
if
(
path_length
>=
sizeof
(
s_un
.
sun_path
))
{
g_set_error
(
error
,
listen_quark
(),
0
,
"unix socket path is too long"
);
return
false
;
}
unlink
(
path
);
s_un
.
sun_family
=
AF_UNIX
;
memcpy
(
s_un
.
sun_path
,
path
,
path_length
+
1
);
success
=
listen_add_address
(
PF_UNIX
,
addrp
,
addrlen
,
error
);
if
(
!
success
)
return
false
;
/* allow everybody to connect */
chmod
(
path
,
0666
);
return
true
;
}
#endif
/* HAVE_UN */
static
bool
static
bool
listen_add_config_param
(
unsigned
int
port
,
listen_add_config_param
(
unsigned
int
port
,
const
struct
config_param
*
param
,
const
struct
config_param
*
param
,
GError
**
error
)
GError
**
error
_r
)
{
{
assert
(
param
!=
NULL
);
assert
(
param
!=
NULL
);
if
(
0
==
strcmp
(
param
->
value
,
"any"
))
{
if
(
0
==
strcmp
(
param
->
value
,
"any"
))
{
return
listen_add_port
(
port
,
error
);
return
server_socket_add_port
(
listen_socket
,
port
,
error_r
);
#ifdef HAVE_UN
}
else
if
(
param
->
value
[
0
]
==
'/'
)
{
}
else
if
(
param
->
value
[
0
]
==
'/'
)
{
return
listen_add_path
(
param
->
value
,
error
);
return
server_socket_add_path
(
listen_socket
,
param
->
value
,
#endif
/* HAVE_UN */
error_r
);
}
else
{
}
else
{
return
listen_add_host
(
param
->
value
,
port
,
error
);
return
server_socket_add_host
(
listen_socket
,
param
->
value
,
port
,
error_r
);
}
}
}
}
...
@@ -358,6 +69,8 @@ listen_global_init(GError **error_r)
...
@@ -358,6 +69,8 @@ listen_global_init(GError **error_r)
bool
success
;
bool
success
;
GError
*
error
=
NULL
;
GError
*
error
=
NULL
;
listen_socket
=
server_socket_new
(
listen_callback
,
NULL
);
if
(
param
!=
NULL
)
{
if
(
param
!=
NULL
)
{
/* "bind_to_address" is configured, create listeners
/* "bind_to_address" is configured, create listeners
for all values */
for all values */
...
@@ -378,7 +91,7 @@ listen_global_init(GError **error_r)
...
@@ -378,7 +91,7 @@ listen_global_init(GError **error_r)
/* no "bind_to_address" configured, bind the
/* no "bind_to_address" configured, bind the
configured port on all interfaces */
configured port on all interfaces */
success
=
listen_add_port
(
port
,
&
erro
r
);
success
=
server_socket_add_port
(
listen_socket
,
port
,
error_
r
);
if
(
!
success
)
{
if
(
!
success
)
{
g_propagate_prefixed_error
(
error_r
,
error
,
g_propagate_prefixed_error
(
error_r
,
error
,
"Failed to listen on *:%d: "
,
"Failed to listen on *:%d: "
,
...
@@ -387,6 +100,9 @@ listen_global_init(GError **error_r)
...
@@ -387,6 +100,9 @@ listen_global_init(GError **error_r)
}
}
}
}
if
(
!
server_socket_open
(
listen_socket
,
error_r
))
return
false
;
listen_port
=
port
;
listen_port
=
port
;
return
true
;
return
true
;
}
}
...
@@ -395,57 +111,7 @@ void listen_global_finish(void)
...
@@ -395,57 +111,7 @@ void listen_global_finish(void)
{
{
g_debug
(
"listen_global_finish called"
);
g_debug
(
"listen_global_finish called"
);
while
(
listen_sockets
!=
NULL
)
{
assert
(
listen_socket
!=
NULL
);
struct
listen_socket
*
ls
=
listen_sockets
;
listen_sockets
=
ls
->
next
;
g_source_remove
(
ls
->
source_id
);
server_socket_free
(
listen_socket
);
close
(
ls
->
fd
);
g_free
(
ls
);
}
}
static
int
get_remote_uid
(
int
fd
)
{
#ifdef HAVE_STRUCT_UCRED
struct
ucred
cred
;
socklen_t
len
=
sizeof
(
cred
);
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_PEERCRED
,
&
cred
,
&
len
)
<
0
)
return
0
;
return
cred
.
uid
;
#else
#ifdef HAVE_GETPEEREID
uid_t
euid
;
gid_t
egid
;
if
(
getpeereid
(
fd
,
&
euid
,
&
egid
)
==
0
)
return
euid
;
#else
(
void
)
fd
;
#endif
return
-
1
;
#endif
}
static
gboolean
listen_in_event
(
G_GNUC_UNUSED
GIOChannel
*
source
,
G_GNUC_UNUSED
GIOCondition
condition
,
gpointer
data
)
{
int
listen_fd
=
GPOINTER_TO_INT
(
data
),
fd
;
struct
sockaddr_storage
sa
;
size_t
sa_length
=
sizeof
(
sa
);
fd
=
accept_cloexec_nonblock
(
listen_fd
,
(
struct
sockaddr
*
)
&
sa
,
&
sa_length
);
if
(
fd
>=
0
)
{
client_new
(
fd
,
(
struct
sockaddr
*
)
&
sa
,
sa_length
,
get_remote_uid
(
fd
));
}
else
if
(
fd
<
0
&&
errno
!=
EINTR
)
{
g_warning
(
"Problems accept()'ing"
);
}
return
true
;
}
}
src/server_socket.c
0 → 100644
View file @
18e3d0b5
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "server_socket.h"
#include "socket_util.h"
#include "fd_util.h"
#include "glib_compat.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "listen"
#define DEFAULT_PORT 6600
struct
one_socket
{
struct
one_socket
*
next
;
struct
server_socket
*
parent
;
unsigned
serial
;
int
fd
;
guint
source_id
;
char
*
path
;
size_t
address_length
;
struct
sockaddr
address
;
};
struct
server_socket
{
server_socket_callback_t
callback
;
void
*
callback_ctx
;
struct
one_socket
*
sockets
,
**
sockets_tail_r
;
unsigned
next_serial
;
};
static
GQuark
server_socket_quark
(
void
)
{
return
g_quark_from_static_string
(
"server_socket"
);
}
struct
server_socket
*
server_socket_new
(
server_socket_callback_t
callback
,
void
*
callback_ctx
)
{
struct
server_socket
*
ss
=
g_new
(
struct
server_socket
,
1
);
ss
->
callback
=
callback
;
ss
->
callback_ctx
=
callback_ctx
;
ss
->
sockets
=
NULL
;
ss
->
sockets_tail_r
=
&
ss
->
sockets
;
ss
->
next_serial
=
1
;
return
ss
;
}
void
server_socket_free
(
struct
server_socket
*
ss
)
{
server_socket_close
(
ss
);
while
(
ss
->
sockets
!=
NULL
)
{
struct
one_socket
*
s
=
ss
->
sockets
;
ss
->
sockets
=
s
->
next
;
assert
(
s
->
fd
<
0
);
g_free
(
s
->
path
);
g_free
(
s
);
}
g_free
(
ss
);
}
/**
* Wraper for sockaddr_to_string() which never fails.
*/
static
char
*
one_socket_to_string
(
const
struct
one_socket
*
s
)
{
char
*
p
=
sockaddr_to_string
(
&
s
->
address
,
s
->
address_length
,
NULL
);
if
(
p
==
NULL
)
p
=
g_strdup
(
"[unknown]"
);
return
p
;
}
static
int
get_remote_uid
(
int
fd
)
{
#ifdef HAVE_STRUCT_UCRED
struct
ucred
cred
;
socklen_t
len
=
sizeof
(
cred
);
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_PEERCRED
,
&
cred
,
&
len
)
<
0
)
return
0
;
return
cred
.
uid
;
#else
#ifdef HAVE_GETPEEREID
uid_t
euid
;
gid_t
egid
;
if
(
getpeereid
(
fd
,
&
euid
,
&
egid
)
==
0
)
return
euid
;
#else
(
void
)
fd
;
#endif
return
-
1
;
#endif
}
static
gboolean
server_socket_in_event
(
G_GNUC_UNUSED
GIOChannel
*
source
,
G_GNUC_UNUSED
GIOCondition
condition
,
gpointer
data
)
{
struct
one_socket
*
s
=
data
;
struct
sockaddr_storage
address
;
size_t
address_length
=
sizeof
(
address
);
int
fd
=
accept_cloexec_nonblock
(
s
->
fd
,
(
struct
sockaddr
*
)
&
address
,
&
address_length
);
if
(
fd
>=
0
)
s
->
parent
->
callback
(
fd
,
(
const
struct
sockaddr
*
)
&
address
,
address_length
,
get_remote_uid
(
fd
),
s
->
parent
->
callback_ctx
);
else
g_warning
(
"accept() failed: %s"
,
g_strerror
(
errno
));
return
true
;
}
bool
server_socket_open
(
struct
server_socket
*
ss
,
GError
**
error_r
)
{
struct
one_socket
*
good
=
NULL
,
*
bad
=
NULL
;
GError
*
last_error
=
NULL
;
for
(
struct
one_socket
*
s
=
ss
->
sockets
;
s
!=
NULL
;
s
=
s
->
next
)
{
assert
(
s
->
serial
>
0
);
assert
(
good
==
NULL
||
s
->
serial
>=
good
->
serial
);
assert
(
s
->
fd
<
0
);
if
(
bad
!=
NULL
&&
s
->
serial
!=
bad
->
serial
)
{
server_socket_close
(
ss
);
g_propagate_error
(
error_r
,
last_error
);
return
false
;
}
GError
*
error
=
NULL
;
s
->
fd
=
socket_bind_listen
(
s
->
address
.
sa_family
,
SOCK_STREAM
,
0
,
&
s
->
address
,
s
->
address_length
,
5
,
&
error
);
if
(
s
->
fd
<
0
)
{
if
(
good
!=
NULL
&&
good
->
serial
==
s
->
serial
)
{
char
*
address_string
=
one_socket_to_string
(
s
);
char
*
good_string
=
one_socket_to_string
(
good
);
g_warning
(
"bind to '%s' failed: %s "
"(continuing anyway, because "
"binding to '%s' succeeded)"
,
address_string
,
error
->
message
,
good_string
);
g_free
(
address_string
);
g_free
(
good_string
);
g_error_free
(
error
);
}
else
if
(
bad
==
NULL
)
{
bad
=
s
;
char
*
address_string
=
one_socket_to_string
(
s
);
g_propagate_prefixed_error
(
&
last_error
,
error
,
"Failed to bind to '%s': "
,
address_string
);
g_free
(
address_string
);
}
else
g_error_free
(
error
);
continue
;
}
/* allow everybody to connect */
if
(
s
->
path
!=
NULL
)
chmod
(
s
->
path
,
0666
);
/* register in the GLib main loop */
GIOChannel
*
channel
=
g_io_channel_unix_new
(
s
->
fd
);
s
->
source_id
=
g_io_add_watch
(
channel
,
G_IO_IN
,
server_socket_in_event
,
s
);
g_io_channel_unref
(
channel
);
/* mark this socket as "good", and clear previous
errors */
good
=
s
;
if
(
bad
!=
NULL
)
{
bad
=
NULL
;
g_error_free
(
last_error
);
last_error
=
NULL
;
}
}
if
(
bad
!=
NULL
)
{
server_socket_close
(
ss
);
g_propagate_error
(
error_r
,
last_error
);
return
false
;
}
return
true
;
}
void
server_socket_close
(
struct
server_socket
*
ss
)
{
for
(
struct
one_socket
*
s
=
ss
->
sockets
;
s
!=
NULL
;
s
=
s
->
next
)
{
if
(
s
->
fd
<
0
)
continue
;
g_source_remove
(
s
->
source_id
);
close
(
s
->
fd
);
s
->
fd
=
-
1
;
}
}
static
struct
one_socket
*
one_socket_new
(
unsigned
serial
,
const
struct
sockaddr
*
address
,
size_t
address_length
)
{
assert
(
address
!=
NULL
);
assert
(
address_length
>
0
);
struct
one_socket
*
s
=
g_malloc
(
sizeof
(
*
s
)
-
sizeof
(
s
->
address
)
+
address_length
);
s
->
next
=
NULL
;
s
->
serial
=
serial
;
s
->
fd
=
-
1
;
s
->
path
=
NULL
;
s
->
address_length
=
address_length
;
memcpy
(
&
s
->
address
,
address
,
address_length
);
return
s
;
}
static
struct
one_socket
*
server_socket_add_address
(
struct
server_socket
*
ss
,
const
struct
sockaddr
*
address
,
size_t
address_length
)
{
assert
(
ss
!=
NULL
);
assert
(
ss
->
sockets_tail_r
!=
NULL
);
assert
(
*
ss
->
sockets_tail_r
==
NULL
);
struct
one_socket
*
s
=
one_socket_new
(
ss
->
next_serial
,
address
,
address_length
);
s
->
parent
=
ss
;
*
ss
->
sockets_tail_r
=
s
;
ss
->
sockets_tail_r
=
&
s
->
next
;
return
s
;
}
#ifdef HAVE_TCP
/**
* Add a listener on a port on all IPv4 interfaces.
*
* @param port the TCP port
*/
static
void
server_socket_add_port_ipv4
(
struct
server_socket
*
ss
,
unsigned
port
)
{
struct
sockaddr_in
sin
;
memset
(
&
sin
,
0
,
sizeof
(
sin
));
sin
.
sin_port
=
htons
(
port
);
sin
.
sin_family
=
AF_INET
;
sin
.
sin_addr
.
s_addr
=
INADDR_ANY
;
server_socket_add_address
(
ss
,
(
const
struct
sockaddr
*
)
&
sin
,
sizeof
(
sin
));
}
#ifdef HAVE_IPV6
/**
* Add a listener on a port on all IPv6 interfaces.
*
* @param port the TCP port
*/
static
void
server_socket_add_port_ipv6
(
struct
server_socket
*
ss
,
unsigned
port
)
{
struct
sockaddr_in6
sin
;
memset
(
&
sin
,
0
,
sizeof
(
sin
));
sin
.
sin6_port
=
htons
(
port
);
sin
.
sin6_family
=
AF_INET6
;
server_socket_add_address
(
ss
,
(
const
struct
sockaddr
*
)
&
sin
,
sizeof
(
sin
));
}
#endif
/* HAVE_IPV6 */
#endif
/* HAVE_TCP */
bool
server_socket_add_port
(
struct
server_socket
*
ss
,
unsigned
port
,
GError
**
error_r
)
{
#ifdef HAVE_TCP
if
(
port
==
0
||
port
>
0xffff
)
{
g_set_error
(
error_r
,
server_socket_quark
(),
0
,
"Invalid TCP port"
);
return
false
;
}
#ifdef HAVE_IPV6
server_socket_add_port_ipv6
(
ss
,
port
);
#endif
server_socket_add_port_ipv4
(
ss
,
port
);
++
ss
->
next_serial
;
return
true
;
#else
/* HAVE_TCP */
(
void
)
ss
;
(
void
)
port
;
g_set_error
(
error_r
,
server_socket_quark
(),
0
,
"TCP support is disabled"
);
return
false
;
#endif
/* HAVE_TCP */
}
bool
server_socket_add_host
(
struct
server_socket
*
ss
,
const
char
*
hostname
,
unsigned
port
,
GError
**
error_r
)
{
#ifdef HAVE_TCP
struct
addrinfo
hints
;
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_flags
=
AI_PASSIVE
;
#ifdef AI_ADDRCONFIG
hints
.
ai_flags
|=
AI_ADDRCONFIG
;
#endif
hints
.
ai_family
=
PF_UNSPEC
;
hints
.
ai_socktype
=
SOCK_STREAM
;
hints
.
ai_protocol
=
IPPROTO_TCP
;
char
service
[
20
];
g_snprintf
(
service
,
sizeof
(
service
),
"%u"
,
port
);
struct
addrinfo
*
ai
;
int
ret
=
getaddrinfo
(
hostname
,
service
,
&
hints
,
&
ai
);
if
(
ret
!=
0
)
{
g_set_error
(
error_r
,
server_socket_quark
(),
ret
,
"Failed to look up host
\"
%s
\"
: %s"
,
hostname
,
gai_strerror
(
ret
));
return
false
;
}
for
(
const
struct
addrinfo
*
i
=
ai
;
i
!=
NULL
;
i
=
i
->
ai_next
)
server_socket_add_address
(
ss
,
i
->
ai_addr
,
i
->
ai_addrlen
);
freeaddrinfo
(
ai
);
++
ss
->
next_serial
;
return
true
;
#else
/* HAVE_TCP */
(
void
)
ss
;
(
void
)
hostname
;
(
void
)
port
;
g_set_error
(
error_r
,
server_socket_quark
(),
0
,
"TCP support is disabled"
);
return
false
;
#endif
/* HAVE_TCP */
}
bool
server_socket_add_path
(
struct
server_socket
*
ss
,
const
char
*
path
,
GError
**
error_r
)
{
#ifdef HAVE_UN
struct
sockaddr_un
s_un
;
size_t
path_length
=
strlen
(
path
);
if
(
path_length
>=
sizeof
(
s_un
.
sun_path
))
{
g_set_error
(
error_r
,
server_socket_quark
(),
0
,
"UNIX socket path is too long"
);
return
false
;
}
unlink
(
path
);
s_un
.
sun_family
=
AF_UNIX
;
memcpy
(
s_un
.
sun_path
,
path
,
path_length
+
1
);
struct
one_socket
*
s
=
server_socket_add_address
(
ss
,
(
const
struct
sockaddr
*
)
&
s_un
,
sizeof
(
s_un
));
s
->
path
=
g_strdup
(
path
);
return
true
;
#else
/* !HAVE_UN */
(
void
)
ss
;
(
void
)
path
;
g_set_error
(
error_r
,
server_socket_quark
(),
0
,
"UNIX domain socket support is disabled"
);
return
false
;
#endif
/* !HAVE_UN */
}
src/server_socket.h
0 → 100644
View file @
18e3d0b5
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_SERVER_SOCKET_H
#define MPD_SERVER_SOCKET_H
#include <stdbool.h>
#include <glib.h>
struct
sockaddr
;
typedef
void
(
*
server_socket_callback_t
)(
int
fd
,
const
struct
sockaddr
*
address
,
size_t
address_length
,
int
uid
,
void
*
ctx
);
struct
server_socket
*
server_socket_new
(
server_socket_callback_t
callback
,
void
*
callback_ctx
);
void
server_socket_free
(
struct
server_socket
*
ss
);
bool
server_socket_open
(
struct
server_socket
*
ss
,
GError
**
error_r
);
void
server_socket_close
(
struct
server_socket
*
ss
);
/**
* Add a listener on a port on all interfaces.
*
* @param port the TCP port
* @param error_r location to store the error occuring, or NULL to
* ignore errors
* @return true on success
*/
bool
server_socket_add_port
(
struct
server_socket
*
ss
,
unsigned
port
,
GError
**
error_r
);
/**
* Resolves a host name, and adds listeners on all addresses in the
* result set.
*
* @param hostname the host name to be resolved
* @param port the TCP port
* @param error_r location to store the error occuring, or NULL to
* ignore errors
* @return true on success
*/
bool
server_socket_add_host
(
struct
server_socket
*
ss
,
const
char
*
hostname
,
unsigned
port
,
GError
**
error_r
);
/**
* Add a listener on a Unix domain socket.
*
* @param path the absolute socket path
* @param error_r location to store the error occuring, or NULL to
* ignore errors
* @return true on success
*/
bool
server_socket_add_path
(
struct
server_socket
*
ss
,
const
char
*
path
,
GError
**
error_r
);
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment