diff options
Diffstat (limited to 'src')
-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 |
5 files changed, 211 insertions, 64 deletions
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 |