139 lines
3.5 KiB
C
139 lines
3.5 KiB
C
/*
|
|
* 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_socket.h"
|
|
#include "gcc.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
|
|
|
|
#if GCC_CHECK_VERSION(4, 2)
|
|
/* allow C99 initialisers on struct sockaddr_in, even if the
|
|
(non-portable) attribute "sin_zero" is missing */
|
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
#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),
|
|
#ifdef WIN32
|
|
0,
|
|
#else
|
|
MSG_DONTWAIT,
|
|
#endif
|
|
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;
|
|
udp->channel = g_io_channel_new_socket(fd);
|
|
/* 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);
|
|
}
|