diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/vdrdiscovery.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/tools/vdrdiscovery.c b/tools/vdrdiscovery.c new file mode 100644 index 00000000..86e1a9a7 --- /dev/null +++ b/tools/vdrdiscovery.c @@ -0,0 +1,240 @@ +/* + * vdrdiscovery.c + * + * Simple broadcast protocol to search VDR with xineliboutput server + * from (local) network. + * + * See the main source file 'xineliboutput.c' for copyright information and + * how to reach the author. + * + * $Id: vdrdiscovery.c,v 1.1 2007-01-01 06:17:48 phintuka Exp $ + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <poll.h> +#include <unistd.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> + +#ifdef FE_STANDALONE +# define LOG_MODULENAME "[discovery] " +#endif + +#define NEED_x_syslog +#define LogToSysLog 1 +#include "../logdefs.h" + +#include "vdrdiscovery.h" + +/* + * + */ + +#ifndef DISCOVERY_PORT +# define DISCOVERY_PORT 37890 +#endif + +/* discovery protocol strings (v1.0) */ +#define DISCOVERY_1_0_HDR "VDR xineliboutput DISCOVERY 1.0" "\r\n" +#define DISCOVERY_1_0_CLI "Client: %s:%d" "\r\n" +#define DISCOVERY_1_0_SVR "Server port: %d" "\r\n" +#define DISCOVERY_1_0_VERSION "Server version: " /*vdr-" VDRVERSION "\r\n\t"*/ \ + "xineliboutput-" XINELIBOUTPUT_VERSION "\r\n" + +/* + * + */ + +static inline int discovery_init(int port) +{ + int fd_discovery = -1; + struct sockaddr_in sin; + + if ((fd_discovery = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_TCP*/)) < 0) { + LOGERR("socket() failed (UDP discovery)"); + } else { + int iBroadcast = 1, iReuse = 1; + if(setsockopt(fd_discovery, SOL_SOCKET, SO_BROADCAST, &iBroadcast, sizeof(int)) < 0) + LOGERR("setsockopt(SO_BROADCAST) failed"); + if(setsockopt(fd_discovery, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0) + LOGERR("setsockopt(SO_REUSEADDR) failed"); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_BROADCAST); + + if (bind(fd_discovery, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + LOGERR("bind() failed (UDP discovery)"); + } else { + return fd_discovery; + } + } + + close(fd_discovery); + return -1; +} + +#ifndef FE_STANDALONE +int udp_discovery_init(void) +{ + return discovery_init(DISCOVERY_PORT); +} +#endif + +static inline int udp_discovery_send(int fd_discovery, int port, char *msg) +{ + struct sockaddr_in sin; + int len = strlen(msg); + + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = INADDR_BROADCAST; + + if(len != sendto(fd_discovery, msg, len, 0, + (struct sockaddr *)&sin, sizeof(sin))) { + LOGERR("UDP broadcast send failed (discovery)"); + return -1; + } + + //LOGDBG("UDP broadcast send succeed (discovery)"); + return 0; +} + +#ifndef FE_STANDALONE +int udp_discovery_broadcast(int fd_discovery, int server_port) +{ + char *msg = NULL; + int result; + + asprintf(&msg, + DISCOVERY_1_0_HDR //"VDR xineliboutput DISCOVERY 1.0" "\r\n" + DISCOVERY_1_0_SVR //"Server port: %d" "\r\n" + DISCOVERY_1_0_VERSION //"Server version: xineliboutput-" XINELIBOUTPUT_VERSION "\r\n" + "\r\n", + server_port); + + result = udp_discovery_send(fd_discovery, DISCOVERY_PORT, msg); + + free(msg); + return result; +} +#else +static inline int udp_discovery_search(int fd_discovery, int port) +{ + char *msg = NULL; + int result; + + asprintf(&msg, + DISCOVERY_1_0_HDR /* "VDR xineliboutput DISCOVERY 1.0" "\r\n" */ + DISCOVERY_1_0_CLI /* "Client: %s:%d" "\r\n" */ + "\r\n", + "255.255.255.255", + port); + + result = udp_discovery_send(fd_discovery, port, msg); + + free(msg); + return result; +} +#endif + +#ifdef FE_STANDALONE +static +#endif +int udp_discovery_recv(int fd_discovery, char *buf, int timeout, + struct sockaddr_in *source) +{ + socklen_t sourcelen = sizeof(struct sockaddr_in); + struct pollfd pfd; + int err; + + pfd.fd = fd_discovery; + pfd.events = POLLIN; + + errno = 0; + err = poll(&pfd, 1, timeout); + if(err < 1) { + if(err < 0) + LOGERR("broadcast poll error"); + return err; + } + + memset(source, 0, sourcelen); + memset(buf, 0, DISCOVERY_MSG_MAXSIZE); + + err = recvfrom(fd_discovery, buf, DISCOVERY_MSG_MAXSIZE-1, 0, + (struct sockaddr *)source, &sourcelen); + + if(err <= 0) + LOGDBG("fd_discovery recvfrom() error"); + + return err; +} + +#ifndef FE_STANDALONE +int udp_discovery_is_valid_search(const char *buf) +{ + const char *id_string = DISCOVERY_1_0_HDR "Client:"; + + if(!strncmp(id_string, buf, strlen(id_string))) { + LOGMSG("Received valid discovery message %s", buf); + return 1; + } + + LOGDBG("BROADCAST: %s", buf); + return 0; +} +#else +int udp_discovery_find_server(int *port, char *address) +{ + static const char *mystring = DISCOVERY_1_0_HDR "Server port: "; + struct sockaddr_in from; + char buf[DISCOVERY_MSG_MAXSIZE]; + int fd_discovery = -1; + int trycount = 0; + int err = 0; + + *port = DISCOVERY_PORT; + strcpy(address, "vdr"); + + if((fd_discovery = discovery_init(DISCOVERY_PORT)) < 0) + return 0; + + while(err >= 0 && ++trycount < 4) { + + if((err = udp_discovery_search(fd_discovery, DISCOVERY_PORT) >= 0)) { + + errno = 0; + while( (err = udp_discovery_recv(fd_discovery, buf, 500, &from)) > 0) { + + uint32_t tmp = ntohl(from.sin_addr.s_addr); + + buf[err] = 0; + LOGDBG("Reveived broadcast: %d bytes from %d.%d.%d.%d \n%s", + err, + ((tmp>>24)&0xff), ((tmp>>16)&0xff), + ((tmp>>8)&0xff), ((tmp)&0xff), + buf); + if(!strncmp(mystring, buf, strlen(mystring))) { + LOGDBG("Valid discovery message"); + close(fd_discovery); + sprintf(address, "%d.%d.%d.%d", + ((tmp>>24)&0xff), ((tmp>>16)&0xff), + ((tmp>>8)&0xff), ((tmp)&0xff)); + if(1 == sscanf(buf + strlen(mystring), "%d", port)) + return 1; + } else { + LOGDBG("NOT valid discovery message"); + } + } + } + } + + /* failed */ + close(fd_discovery); + return 0; +} +#endif + |