diff options
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | src/ntp_server.c | 79 | ||||
-rw-r--r-- | src/ntp_server.h | 8 | ||||
-rw-r--r-- | src/output/raop_output_plugin.c | 6 | ||||
-rw-r--r-- | src/udp_server.c | 130 | ||||
-rw-r--r-- | src/udp_server.h | 52 | ||||
-rw-r--r-- | test/run_ntp_server.c | 83 |
7 files changed, 219 insertions, 142 deletions
diff --git a/Makefile.am b/Makefile.am index b6947e137..dae71c209 100644 --- a/Makefile.am +++ b/Makefile.am @@ -294,6 +294,7 @@ src_mpd_SOURCES = \ src/client_message.c \ src/client_subscribe.h \ src/client_subscribe.c \ + src/udp_server.c src/udp_server.h \ src/server_socket.c \ src/listen.c \ src/log.c \ @@ -1016,6 +1017,7 @@ test_run_ntp_server_LDADD = $(MPD_LIBS) \ test_run_ntp_server_SOURCES = test/run_ntp_server.c \ test/signals.c test/signals.h \ src/io_thread.c src/io_thread.h \ + src/udp_server.c src/udp_server.h \ src/ntp_server.c src/ntp_server.h test_run_filter_CPPFLAGS = $(AM_CPPFLAGS) @@ -1122,6 +1124,7 @@ test_run_output_SOURCES = test/run_output.c \ test/stdbin.h \ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ src/io_thread.c src/io_thread.h \ + src/udp_server.c src/udp_server.h \ src/audio_check.c \ src/audio_format.c \ src/audio_parser.c \ diff --git a/src/ntp_server.c b/src/ntp_server.c index d2e45bcac..c33c0a933 100644 --- a/src/ntp_server.c +++ b/src/ntp_server.c @@ -18,7 +18,7 @@ */ #include "ntp_server.h" -#include "io_thread.h" +#include "udp_server.h" #include <glib.h> #include <assert.h> @@ -76,18 +76,18 @@ fill_time_buffer(unsigned char *buffer) fill_time_buffer_with_time(buffer, ¤t_time); } -static bool -ntp_server_handle(struct ntp_server *ntp) +static void +ntp_server_datagram(int fd, const void *data, size_t num_bytes, + const struct sockaddr *source_address, + size_t source_address_length, G_GNUC_UNUSED void *ctx) { unsigned char buf[32]; - struct sockaddr addr; int iter; - socklen_t addr_len = sizeof(addr); - ssize_t num_bytes = recvfrom(ntp->fd, (void *)buf, sizeof(buf), 0, - &addr, &addr_len); - if (num_bytes == 0) { - return false; - } + + if (num_bytes > sizeof(buf)) + num_bytes = sizeof(buf); + memcpy(buf, data, num_bytes); + fill_time_buffer(buf + 16); // set to response buf[1] = 0xd3; @@ -97,65 +97,36 @@ ntp_server_handle(struct ntp_server *ntp) } fill_time_buffer(buf + 24); - num_bytes = sendto(ntp->fd, (void *)buf, num_bytes, 0, - &addr, addr_len); - - return num_bytes == sizeof(buf); + sendto(fd, (void *)buf, num_bytes, 0, + source_address, source_address_length); } -static gboolean -ntp_in_event(G_GNUC_UNUSED GIOChannel *source, - G_GNUC_UNUSED GIOCondition condition, - gpointer data) -{ - struct ntp_server *ntp = data; - - ntp_server_handle(ntp); - return true; -} +static const struct udp_server_handler ntp_server_handler = { + .datagram = ntp_server_datagram, +}; void ntp_server_init(struct ntp_server *ntp) { ntp->port = 6002; - ntp->fd = -1; + ntp->udp = NULL; } -void -ntp_server_open(struct ntp_server *ntp, int fd) +bool +ntp_server_open(struct ntp_server *ntp, GError **error_r) { - assert(ntp->fd < 0); - assert(fd >= 0); + assert(ntp->udp == NULL); - ntp->fd = fd; - -#ifndef G_OS_WIN32 - ntp->channel = g_io_channel_unix_new(fd); -#else - ntp->channel = g_io_channel_win32_new_socket(fd); -#endif - /* NULL encoding means the stream is binary safe */ - g_io_channel_set_encoding(ntp->channel, NULL, NULL); - /* no buffering */ - g_io_channel_set_buffered(ntp->channel, false); - - ntp->source = g_io_create_watch(ntp->channel, G_IO_IN); - g_source_set_callback(ntp->source, (GSourceFunc)ntp_in_event, ntp, - NULL); - g_source_attach(ntp->source, io_thread_context()); + ntp->udp = udp_server_new(ntp->port, &ntp_server_handler, ntp, + error_r); + return ntp->udp != NULL; } void ntp_server_close(struct ntp_server *ntp) { - if (ntp->source != NULL) { - g_source_destroy(ntp->source); - g_source_unref(ntp->source); + if (ntp->udp != NULL) { + udp_server_free(ntp->udp); + ntp->udp = NULL; } - - if (ntp->channel != NULL) - g_io_channel_unref(ntp->channel); - - if (ntp->fd >= 0) - close(ntp->fd); } diff --git a/src/ntp_server.h b/src/ntp_server.h index 2b970dff2..fe6f8083b 100644 --- a/src/ntp_server.h +++ b/src/ntp_server.h @@ -28,17 +28,15 @@ struct timeval; struct ntp_server { unsigned short port; - int fd; - GIOChannel *channel; - GSource *source; + struct udp_server *udp; }; void ntp_server_init(struct ntp_server *ntp); -void -ntp_server_open(struct ntp_server *ntp, int fd); +bool +ntp_server_open(struct ntp_server *ntp, GError **error_r); void ntp_server_close(struct ntp_server *ntp); diff --git a/src/output/raop_output_plugin.c b/src/output/raop_output_plugin.c index ee5f67411..7ff5b009a 100644 --- a/src/output/raop_output_plugin.c +++ b/src/output/raop_output_plugin.c @@ -799,13 +799,9 @@ raop_output_open(void *data, struct audio_format *audio_format, GError **error_r if (raop_session->data_fd < 0) return false; - int fd = open_udp_socket(NULL, &raop_session->ntp.port, - error_r); - if (fd < 0) + if (!ntp_server_open(&raop_session->ntp, error_r)) return false; - ntp_server_open(&raop_session->ntp, fd); - raop_session->ctrl.fd = open_udp_socket(NULL, &raop_session->ctrl.port, error_r); diff --git a/src/udp_server.c b/src/udp_server.c new file mode 100644 index 000000000..05779138b --- /dev/null +++ b/src/udp_server.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2003-2011 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 "udp_server.h" +#include "io_thread.h" + +#include <glib.h> +#include <unistd.h> +#include <sys/time.h> +#include <errno.h> + +#ifdef WIN32 +#define WINVER 0x0501 +#include <ws2tcpip.h> +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +struct udp_server { + const struct udp_server_handler *handler; + void *handler_ctx; + + int fd; + GIOChannel *channel; + GSource *source; + + char buffer[8192]; +}; + +static gboolean +udp_in_event(G_GNUC_UNUSED GIOChannel *source, + G_GNUC_UNUSED GIOCondition condition, + gpointer data) +{ + struct udp_server *udp = data; + + struct sockaddr_storage address_storage; + struct sockaddr *address = (struct sockaddr *)&address_storage; + socklen_t address_length = sizeof(address_storage); + + ssize_t nbytes = recvfrom(udp->fd, udp->buffer, sizeof(udp->buffer), + MSG_DONTWAIT, + address, &address_length); + if (nbytes <= 0) + return true; + + udp->handler->datagram(udp->fd, udp->buffer, nbytes, + address, address_length, udp->handler_ctx); + return true; +} + +struct udp_server * +udp_server_new(unsigned port, + const struct udp_server_handler *handler, void *ctx, + GError **error_r) +{ + int fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + g_set_error(error_r, udp_server_quark(), errno, + "failed to create UDP socket: %s", + g_strerror(errno)); + return NULL; + } + + const struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = htonl(INADDR_ANY), + }, + .sin_port = htons(port), + }; + + if (bind(fd, (const struct sockaddr *)&address, sizeof(address)) < 0) { + g_set_error(error_r, udp_server_quark(), errno, + "failed to bind UDP port %u: %s", + port, g_strerror(errno)); + close(fd); + return NULL; + } + + struct udp_server *udp = g_new(struct udp_server, 1); + udp->handler = handler; + udp->handler_ctx = ctx; + + udp->fd = fd; +#ifndef G_OS_WIN32 + udp->channel = g_io_channel_unix_new(fd); +#else + udp->channel = g_io_channel_win32_new_socket(fd); +#endif + /* NULL encoding means the stream is binary safe */ + g_io_channel_set_encoding(udp->channel, NULL, NULL); + /* no buffering */ + g_io_channel_set_buffered(udp->channel, false); + + udp->source = g_io_create_watch(udp->channel, G_IO_IN); + g_source_set_callback(udp->source, (GSourceFunc)udp_in_event, udp, + NULL); + g_source_attach(udp->source, io_thread_context()); + + return udp; +} + +void +udp_server_free(struct udp_server *udp) +{ + g_source_destroy(udp->source); + g_source_unref(udp->source); + g_io_channel_unref(udp->channel); + close(udp->fd); + g_free(udp); +} diff --git a/src/udp_server.h b/src/udp_server.h new file mode 100644 index 000000000..9e3471a45 --- /dev/null +++ b/src/udp_server.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2011 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_UDP_SERVER_H +#define MPD_UDP_SERVER_H + +#include <glib.h> + +#include <stddef.h> + +struct sockaddr; + +struct udp_server_handler { + /** + * A datagram was received. + */ + void (*datagram)(int fd, const void *data, size_t length, + const struct sockaddr *source_address, + size_t source_address_length, void *ctx); +}; + +static inline GQuark +udp_server_quark(void) +{ + return g_quark_from_static_string("udp_server"); +} + +struct udp_server * +udp_server_new(unsigned port, + const struct udp_server_handler *handler, void *ctx, + GError **error_r); + +void +udp_server_free(struct udp_server *udp); + +#endif diff --git a/test/run_ntp_server.c b/test/run_ntp_server.c index db24059ab..6f732a074 100644 --- a/test/run_ntp_server.c +++ b/test/run_ntp_server.c @@ -46,78 +46,6 @@ on_quit(void) io_thread_quit(); } -static int bind_host(int sd, char *hostname, unsigned long ulAddr, - unsigned short *port) -{ - struct sockaddr_in my_addr; - socklen_t nlen = sizeof(struct sockaddr); - struct hostent *h; - - memset(&my_addr, 0, sizeof(my_addr)); - /* use specified hostname */ - if (hostname) { - /* get server IP address (no check if input is IP address or DNS name) */ - h = gethostbyname(hostname); - if (h == NULL) { - if (strstr(hostname, "255.255.255.255") == hostname) { - my_addr.sin_addr.s_addr=-1; - } else { - if ((my_addr.sin_addr.s_addr = inet_addr(hostname)) == 0xFFFFFFFF) { - return -1; - } - } - my_addr.sin_family = AF_INET; - } else { - my_addr.sin_family = h->h_addrtype; - memcpy((char *) &my_addr.sin_addr.s_addr, - h->h_addr_list[0], h->h_length); - } - } else { - // if hostname=NULL, use INADDR_ANY - if (ulAddr) - my_addr.sin_addr.s_addr = ulAddr; - else - my_addr.sin_addr.s_addr = htonl(INADDR_ANY); - my_addr.sin_family = AF_INET; - } - - /* bind a specified port */ - my_addr.sin_port = htons(*port); - - if (bind(sd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) { - return -1; - } - - if (*port == 0) { - getsockname(sd, (struct sockaddr *) &my_addr, &nlen); - *port = ntohs(my_addr.sin_port); - } - - return 0; -} - -static int -open_udp_socket(char *hostname, unsigned short *port) -{ - int sd; - int size = 30000; - - /* socket creation */ - sd = socket(PF_INET, SOCK_DGRAM, 0); - if (sd < 0) { - return -1; - } - if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) < 0) { - return -1; - } - if (bind_host(sd, hostname, 0, port)) { - close(sd); - return -1; - } - - return sd; -} - int main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) { @@ -128,15 +56,14 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) struct ntp_server ntp; ntp_server_init(&ntp); - int fd = open_udp_socket(NULL, &ntp.port); - if (fd < 0) { - g_printerr("Failed to create UDP socket\n"); - ntp_server_close(&ntp); + GError *error = NULL; + if (!ntp_server_open(&ntp, &error)) { + io_thread_deinit(); + g_printerr("%s\n", error->message); + g_error_free(error); return EXIT_FAILURE; } - ntp_server_open(&ntp, fd); - io_thread_run(); ntp_server_close(&ntp); |