OBJS = $(PLUGIN).o
# VOMP-INSERT
-OBJS += dsock.o mvpserver.o udpreplier.o bootpd.o tftpd.o i18n.o vompclient.o tcp.o \
- ringbuffer.o mvprelay.o vompclientrrproc.o \
+OBJS += dsock.o dsock6.o mvpserver.o udpreplier.o udp6replier.o bootpd.o tftpd.o i18n.o \
+ vompclient.o tcp.o ringbuffer.o mvprelay.o vompclientrrproc.o \
config.o log.o thread.o tftpclient.o \
media.o responsepacket.o \
mediafile.o mediaplayer.o servermediafile.o serialize.o medialauncher.o \
--- /dev/null
+/*
+ Copyright 2019 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <vector>
+#include <algorithm>
+
+#include "dsock6.h"
+
+DatagramSocket6::DatagramSocket6()
+{
+ addrlen = sizeof(struct sockaddr_in6);
+ log = Log::getInstance();
+ initted = 0;
+}
+
+DatagramSocket6::~DatagramSocket6()
+{
+ shutdown();
+}
+
+bool DatagramSocket6::init(USHORT port)
+{
+ if ((socketnum = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ {
+ log->log("UDP6", Log::CRIT, "Socket error");
+ return false;
+ }
+
+ char* one = (char*)1;
+ if (setsockopt(socketnum, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
+ {
+ log->log("UDP6", Log::CRIT, "Reuse addr fail");
+ return false;
+ }
+
+ struct sockaddr_in6 saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin6_family = AF_INET6;
+ saddr.sin6_port = htons(port);
+ saddr.sin6_addr = in6addr_any;
+
+ if (bind(socketnum, (struct sockaddr *)&saddr, addrlen) == -1)
+ {
+ log->log("UDP6", Log::CRIT, "Bind error %u", port);
+ close(socketnum);
+ return false;
+ }
+
+ struct ifaddrs* ifas;
+ if(getifaddrs(&ifas))
+ {
+ log->log("UDP6", Log::CRIT, "getifaddrs error");
+ close(socketnum);
+ return false;
+ }
+
+ struct if_nameindex* ifs = if_nameindex();
+ std::vector<unsigned int> mcastIndexes;
+
+ struct ifaddrs* ifaNext = ifas;
+ for(int i = 0; ifaNext; i++)
+ {
+ if ((ifaNext->ifa_flags & IFF_MULTICAST) && (ifaNext->ifa_addr->sa_family == AF_INET6))
+ {
+ for(int i = 0; ifs[i].if_index > 0; i++)
+ {
+ if (!strcmp(ifaNext->ifa_name, ifs[i].if_name))
+ {
+ mcastIndexes.push_back(ifs[i].if_index);
+ break;
+ }
+ }
+ }
+ ifaNext = ifaNext->ifa_next;
+ }
+
+ freeifaddrs(ifas);
+ if_freenameindex(ifs);
+
+
+ std::sort(mcastIndexes.begin(), mcastIndexes.end());
+ auto last = std::unique(mcastIndexes.begin(), mcastIndexes.end());
+ mcastIndexes.erase(last, mcastIndexes.end());
+
+
+ for(auto mif : mcastIndexes)
+ {
+ //log->log("UDP6", Log::DEBUG, "To listen on index %u", mif);
+
+ struct ipv6_mreq mGroup;
+ inet_pton(AF_INET6, "ff15:766f:6d70:2064:6973:636f:7665:7279", &mGroup.ipv6mr_multiaddr);
+ mGroup.ipv6mr_interface = mif;
+
+ if (!setsockopt(socketnum, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mGroup, sizeof(mGroup)))
+ log->log("UDP6", Log::DEBUG, "Listening on IF %u", mif);
+ else
+ log->log("UDP6", Log::ERR, "Cannot listen on IF %u", mif);
+ }
+
+
+ FD_ZERO(&readfds);
+ FD_SET(socketnum, &readfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ initted = 1;
+ return true;
+}
+
+void DatagramSocket6::shutdown()
+{
+ if (initted) close(socketnum);
+ initted = 0;
+}
+
+unsigned char DatagramSocket6::waitforMessage(unsigned char how)
+{
+ if (!initted) return 0;
+
+ /* how = 0 - block
+ how = 1 - start new wait
+ how = 2 - continue wait
+ */
+
+ struct timeval* passToSelect = NULL;
+
+
+ if (how == 0)
+ {
+ passToSelect = NULL;
+ }
+ else if (how == 1)
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 100000;
+ passToSelect = &tv;
+ }
+ else if (how == 2)
+ {
+ if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) // protection in case timer = 0
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 100000;
+ }
+ passToSelect = &tv;
+ }
+ FD_ZERO(&readfds);
+ FD_SET(socketnum, &readfds);
+
+ if (select(socketnum + 1, &readfds, NULL, NULL, passToSelect) <= 0) return 1;
+
+ struct sockaddr_in6 theirAddr;
+ if ((mlength = recvfrom(socketnum, buf, MAXBUFLEN, 0, (struct sockaddr *)&theirAddr, &addrlen)) == -1)
+ {
+ log->log("UDP6", Log::DEBUG, "recvfrom error");
+ return 0;
+ }
+ else
+ {
+ memset(&buf[mlength], 0, MAXBUFLEN - mlength);
+ inet_ntop(AF_INET6, &theirAddr.sin6_addr, fromIPA, 40);
+ fromPort = ntohs(theirAddr.sin6_port);
+ log->log("UDP", Log::DEBUG, "%s:%i received length %i", fromIPA, fromPort, mlength);
+ return 2;
+ }
+
+ /* Return 0, failure
+ Return 1, nothing happened, timer expired
+ Return 2, packet arrived (timer not expired)
+ */
+}
+
+void DatagramSocket6::send(const char *ipa, USHORT port, char *message, int length)
+{
+ int sentLength = 0;
+
+ struct sockaddr_in6 theirAddr;
+ memset(&theirAddr, 0, sizeof(struct sockaddr_in6));
+
+ theirAddr.sin6_family = AF_INET6;
+ theirAddr.sin6_port = htons(port);
+ inet_pton(AF_INET6, ipa, &theirAddr.sin6_addr);
+
+ sentLength = sendto(socketnum, message, length, 0, (struct sockaddr *)&theirAddr, addrlen);
+ if (sentLength == length)
+ {
+ log->log("UDP", Log::DEBUG, "%s:%u sent length %i", ipa, port, length);
+ return;
+ }
+
+ log->log("UDP", Log::DEBUG, "%s:%u send failed %i", ipa, port, length);
+
+ sentLength = sendto(socketnum, message, length, 0, (struct sockaddr *)&theirAddr, addrlen);
+ if (sentLength == length)
+ {
+ log->log("UDP", Log::DEBUG, "%s:%u sent length %i 2nd try", ipa, port, length);
+ return;
+ }
+
+ log->log("UDP", Log::DEBUG, "%s:%u send failed %i 2nd try", ipa, port, length);
+}
--- /dev/null
+/*
+ Copyright 2019 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef DSOCK6_H
+#define DSOCK6_H
+
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include "defines.h"
+#include "log.h"
+
+#define MAXBUFLEN 2000
+
+class DatagramSocket6
+{
+ public:
+ DatagramSocket6();
+ ~DatagramSocket6();
+ bool init(USHORT port);
+ void shutdown();
+ unsigned char waitforMessage(unsigned char); // int =0-block =1-new wait =2-continue wait
+ void send(const char *, USHORT, char *, int); // send wants: IP Address ddn style, port,
+ // data, length of data
+
+ char* getData() { return buf; } // returns a pointer to the data
+ char* getFromIPA() { return fromIPA; } // returns a pointer to from IP address
+ USHORT getFromPort() const { return fromPort; } // returns the sender port number
+ int getDataLength() const { return mlength; } // returns data length
+
+ private:
+ Log* log;
+ int initted;
+ int socketnum; // Socket descriptor
+
+ socklen_t addrlen; // length of sockaddr struct
+ char buf[MAXBUFLEN]; // main data buffer
+ char fromIPA[40]; // from string (ip address)
+ USHORT fromPort; // which port user sent on
+ int mlength; // length of message
+ struct timeval tv;
+ fd_set readfds;
+};
+
+#endif
/*
- Copyright 2004-2005 Chris Tallon
+ Copyright 2004-2019 Chris Tallon
This file is part of VOMP.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with VOMP; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
*/
#include "mvpserver.h"
extern pthread_mutex_t threadClientMutex;
+#define UDP6PORT 51056
+
MVPServer::MVPServer()
{
// MH in case anbody has a better position :-)
close(listeningSocket);
udpr.shutdown();
+ udpr6.shutdown();
bootpd.shutdown();
tftpd.shutdown();
mvprelay.shutdown();
if (fail) tcpServerPort = 3024;
int udpSuccess = udpr.run(udpport, serverName, tcpServerPort);
+ int udp6Success = udpr6.run(UDP6PORT, serverName, tcpServerPort);
delete[] serverName;
return 0;
}
+ if (!udp6Success)
+ {
+ log.log("Main", Log::CRIT, "Could not start UDP6 replier");
+ stop();
+ return 0;
+ }
+
// Read config and start bootp and tftp as appropriate
char* configString;
#include "defines.h"
#include "udpreplier.h"
+#include "udp6replier.h"
#include "mvprelay.h"
#include "bootpd.h"
#include "tftpd.h"
Log log;
Config config;
UDPReplier udpr;
+ UDP6Replier udpr6;
Bootpd bootpd;
Tftpd tftpd;
MVPRelay mvprelay;
--- /dev/null
+/*
+ Copyright 2019 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "vompclientrrproc.h"
+
+#include "udp6replier.h"
+
+UDP6Replier::UDP6Replier()
+{
+ message = NULL;
+ messageLen = 0;
+}
+
+UDP6Replier::~UDP6Replier()
+{
+ shutdown();
+}
+
+int UDP6Replier::shutdown()
+{
+ if (threadIsActive()) threadCancel();
+
+ if (message) delete[] message;
+ message = NULL;
+ return 1;
+}
+
+int UDP6Replier::run(USHORT port, char* serverName, USHORT serverPort)
+{
+ if (threadIsActive()) return 1;
+
+ /*
+ VOMP Discovery Protocol V1
+
+ Client transmits: "VDP-0001\0<6-byte MAC>"...
+ ... for IPv4: broadcasts on ports 51051-51055
+ ... for IPv6: multicasts to ff15:766f:6d70:2064:6973:636f:7665:7279 port 51056
+
+ Server responds:
+
+ Field 1 p0: 9 bytes "VDP-0002\0"
+
+ Field 2 p9, 1 byte:
+
+ 0 = no IP specified
+ 4 = first 4 bytes of field 3 are IPv4 address of server
+ 6 = field 3 16 bytes are IPv6 address of server
+
+ Field 3, p10, 16 bytes:
+ As described above. If field 2 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
+
+ Field 4 p26, 2 bytes:
+ Port number of server
+
+ Field 5 p28, 4 bytes:
+ VOMP protocol version (defined in vdr.cc)
+
+ Field 6 p32, variable length
+ String of server name, null terminated
+ */
+
+ messageLen = strlen(serverName) + 33;
+ message = new char[messageLen];
+ memset(message, 0, messageLen);
+ // by zeroing the packet, this sets no ip address return information
+
+ strcpy(message, "VDP-0002");
+
+ USHORT temp = htons(serverPort);
+ memcpy(&message[26], &temp, 2);
+
+ ULONG temp2 = htonl(VompClientRRProc::getProtocolVersionMin());
+ memcpy(&message[28], &temp2, 4);
+
+ strcpy(&message[32], serverName);
+ // Fix Me add also the maximum version somewhere
+ if (!ds.init(port))
+ {
+ shutdown();
+ return 0;
+ }
+
+ if (!threadStart())
+ {
+ shutdown();
+ return 0;
+ }
+
+ Log::getInstance()->log("UDP6Replier", Log::DEBUG, "UDP replier started");
+ return 1;
+}
+
+void UDP6Replier::threadMethod()
+{
+ int retval;
+ while(1)
+ {
+ retval = ds.waitforMessage(0);
+ if (retval == 1) continue;
+
+ if (!strncmp(ds.getData(), "VDP-0001", 8))
+ {
+ Log::getInstance()->log("UDP6Replier", Log::DEBUG, "UDP6 request from %s", ds.getFromIPA());
+ ds.send(ds.getFromIPA(), ds.getFromPort(), message, messageLen);
+ }
+ }
+}
--- /dev/null
+/*
+ Copyright 2019 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP 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.
+
+ VOMP 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 VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef UDP6REPLIER_H
+#define UDP6REPLIER_H
+
+#include "log.h"
+#include "dsock6.h"
+#include "thread.h"
+
+class UDP6Replier : public Thread
+{
+ public:
+ UDP6Replier();
+ virtual ~UDP6Replier();
+
+ int run(USHORT udpPort, char* serverName, USHORT serverPort);
+ int shutdown();
+
+ private:
+ void threadMethod();
+
+ DatagramSocket6 ds;
+ char* message;
+ int messageLen;
+};
+
+#endif
/*
VOMP Discovery Protocol V1
- Client transmits: "VDP-0001\0<6-byte MAC>" on ports 51051-51055
+ Client transmits: "VDP-0001\0<6-byte MAC>"...
+ ... for IPv4: broadcasts on ports 51051-51055
+ ... for IPv6: multicasts to ff15:766f:6d70:2064:6973:636f:7665:7279 port 51051
Server responds:
Field 1 p0: 9 bytes "VDP-0002\0"
-
+
Field 2 p9, 1 byte:
0 = no IP specified
- 4 = first 4 bytes of field 2 are IPv4 address of server
- 6 = field 2 16 bytes are IPv6 address of server
+ 4 = first 4 bytes of field 3 are IPv4 address of server
+ 6 = field 3 16 bytes are IPv6 address of server
Field 3, p10, 16 bytes:
- As described above. If field 1 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
+ As described above. If field 2 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
Field 4 p26, 2 bytes:
Port number of server
Field 5 p28, 4 bytes:
VOMP protocol version (defined in vdr.cc)
-
+
Field 6 p32, variable length
String of server name, null terminated
*/