]> git.vomp.tv Git - vompclient.git/commitdiff
Rewrite server discovery code. Move from VDR to new VDPC class
authorChris Tallon <chris@vomp.tv>
Sat, 28 Mar 2020 15:25:00 +0000 (15:25 +0000)
committerChris Tallon <chris@vomp.tv>
Sat, 28 Mar 2020 15:25:00 +0000 (15:25 +0000)
21 files changed:
GNUmakefile
dsock.cc [deleted file]
dsock.h [deleted file]
inputman.cc
inputudp.cc
inputudp.h
objects.mk
udp4.cc [new file with mode: 0644]
udp4.h [new file with mode: 0644]
udp6.cc [new file with mode: 0644]
udp6.h [new file with mode: 0644]
vconnect.cc
vconnect.h
vdp6.cc [deleted file]
vdp6.h [deleted file]
vdpc.cc [new file with mode: 0644]
vdpc.h [new file with mode: 0644]
vdr.cc
vdr.h
vserverselect.cc
vserverselect.h

index bec58645ea8f10efaacd6e89d63b11dbbf04ea71..8b8579fb9fa10ab806e09a56d3975f099162efa1 100644 (file)
@@ -55,7 +55,7 @@ LD=g++
 LDFLAGS = -fuse-ld=gold $(PICTURES)
 #-fuse-ld=gold
 
-vomp_options += -DIPV6
+vomp_options += -DIPV4 -DIPV6
 LIBPATHS = -L/opt/vc/lib -L/usr/lib/arm-linux-gnueabihf
 LIBS = -lpthread -lrt -lbrcmEGL -lbrcmOpenVG -lopenmaxil -lbcm_host -lavformat -lavcodec -lavutil -lavresample
 LIBS += -ldl -lfontconfig -lfreetype -lMagick++-6.Q16
diff --git a/dsock.cc b/dsock.cc
deleted file mode 100644 (file)
index c371009..0000000
--- a/dsock.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
-    Copyright 2004-2005 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 "dsock.h"
-
-DatagramSocket::DatagramSocket(short port)
-: myPort(port)
-{
-  addrlen = sizeof(struct sockaddr);
-}
-
-DatagramSocket::~DatagramSocket()
-{
-  if (initted) shutdown();
-}
-
-int DatagramSocket::init()
-{
-  if (initted) return 0;
-
-  if ((socketnum = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
-  { perror("socket"); return 0; }
-
-  myAddr.sin_family = AF_INET;         // host byte order
-  myAddr.sin_port = htons(myPort);     // short, network byte order
-  myAddr.sin_addr.s_addr = getIPNumber(iterate_ip++); // auto-fill with my IP
-  memset(&(myAddr.sin_zero), 0, 8);    // zero the rest of the struct
-
-  if (bind(socketnum, reinterpret_cast<struct sockaddr *>(&myAddr), addrlen) == -1)
-  { perror("bind"); return 0; }
-
-  FD_ZERO(&readfds);
-  FD_SET(socketnum, &readfds);
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  int allowed = 1;
-
-#ifdef WIN32
-  setsockopt(socketnum, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&allowed), sizeof(allowed));
-#else
-  setsockopt(socketnum, SOL_SOCKET, SO_BROADCAST, static_cast<void*>(&allowed), sizeof(allowed));
-#endif
-
-  initted = true;
-
-  return 1;
-}
-
-void DatagramSocket::shutdown()
-{
-  if (!initted) return;
-  CLOSESOCKET(socketnum);
-  initted = false;
-}
-
-#ifdef WIN32
-unsigned char DatagramSocket::waitforMessage(unsigned char how, SOCKET quitPipe)
-#else
-unsigned char DatagramSocket::waitforMessage(unsigned char how, int quitPipe)
-#endif
-{
-  if (!initted) return 0;
-
-  /* how = 0 - block
-     how = 1 - start new wait
-     how = 2 - continue wait
-     how = 3 - block, return on byte from quitPipe
-  */
-
-  FD_ZERO(&readfds);
-  FD_SET(socketnum, &readfds);
-
-  struct timeval* passToSelect = NULL;
-
-  int sockMaxP1 = socketnum + 1;
-
-  if (how == 1)
-  {
-    tv.tv_sec = 1;
-    tv.tv_usec = 500000;
-    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 = 500000;
-    }
-    passToSelect = &tv;
-  }
-  else if (how == 3)
-  {
-    FD_SET(quitPipe, &readfds);
-    if (quitPipe > socketnum) sockMaxP1 = quitPipe + 1;
-  }
-
-
-  if (select(sockMaxP1, &readfds, NULL, NULL, passToSelect) <= 0)
-  {  return 1;  }
-
-  if ((how == 3) && FD_ISSET(quitPipe, &readfds)) return 3;
-
-  if ((mlength = recvfrom(socketnum, buf, MAXBUFLEN, 0,
-      reinterpret_cast<struct sockaddr *>(&theirAddr), &addrlen)) == -1)
-  { perror("recvfrom"); return 0; }
-  else
-  {
-    memset(&buf[mlength], 0, MAXBUFLEN - mlength);
-    strcpy(fromIPA, inet_ntoa(theirAddr.sin_addr));
-    fromPort = ntohs(theirAddr.sin_port);
-
-    if (DSOCKDEBUG)
-    {
-      //printf("%s:%i\tIN  %i\t", fromIPA, fromPort, mlength);
-      int k;
-      for(k = 0; k < mlength; k++)
-        printf("%u ", static_cast<unsigned char>(buf[k]));
-      printf("\n");
-    }
-    return 2;
-  }
-
-  /* Return 0, failure
-     Return 1, nothing happened, timer expired
-     Return 2, packet arrived (timer not expired)
-  */
-}
-
-UINT DatagramSocket::getDataLength(void) const
-{
-  return static_cast<UINT>(mlength);
-}
-
-const void* DatagramSocket::getData() const     {  return buf;  }
-const char* DatagramSocket::getFromIPA() const  {  return fromIPA;  }
-short DatagramSocket::getFromPort() const       {  return fromPort; }
-
-void DatagramSocket::send(const char *ipa, short port, char *message, int length)
-{
-  if (!initted) return;
-
-  if (DSOCKDEBUG)
-  {
-    printf("%s:%i\tOUT %i\t", ipa, port, length);
-    int k;
-    UCHAR l;
-    for (k = 0; k < length; k++)
-      { l = static_cast<UCHAR>(message[k]); printf("%u ", l); }
-  }
-
-  int sentLength = 0;
-
-  theirAddr.sin_family = AF_INET;      // host byte order
-  theirAddr.sin_port = htons(port);    // short, network byte order
-  struct in_addr tad;                  // temp struct tad needed to pass to theirAddr.sin_addr
-  tad.s_addr = inet_addr(ipa);
-  theirAddr.sin_addr = tad;            // address
-  memset(&(theirAddr.sin_zero), 0, 8); // zero the rest of the struct
-
-  errno = 0;
-
-  sentLength = sendto(socketnum, message, length, 0, reinterpret_cast<struct sockaddr *>(&theirAddr), addrlen);
-  if (sentLength == length)
-  {
-    if (DSOCKDEBUG) printf(" GOOD\n");
-  }
-  else
-  {
-    if (DSOCKDEBUG)
-    {
-      printf(" --BAD--");  fflush(stdout);
-    }
-    sentLength = sendto(socketnum, message, length, 0, reinterpret_cast<struct sockaddr *>(&theirAddr), addrlen);
-    if (sentLength == length)
-    {
-      if (DSOCKDEBUG) printf(" GOOD\n");
-    }
-    else
-    {
-      if (DSOCKDEBUG && (sentLength != length))
-      {
-
-        printf(" -#-FAILED-#-\n");
-        printf("--------------\n");
-        printf("Sendto failure\n");
-        printf("--------------\n");
-        printf("%s:%i\tOUT %i %i ...\n", ipa, port, length, sentLength);
-        perror("Perror reports");
-        printf("errno value: %d\n", errno);
-        printf("errno translated: %s\n", strerror(errno));
-      //  printf("h_errno value: %d\n", h_errno);
-      //  printf("\nActual address: %s\n", inet_ntoa(tad));
-      //  printf("Actual port: %i\n", ntohs(theirAddr.sin_port));
-        printf("continuing...\n\n");
-
-      }
-    }
-  }
-}
-
-#ifndef WIN32
-ULONG DatagramSocket::getIPNumber(ULONG)
-{
-  return INADDR_ANY;
-}
-#else
-ULONG DatagramSocket::getIPNumber(ULONG num)
-{
-  char buffer[100];
-  ULONG returnaddress;
-
-  if (gethostname(buffer,sizeof(buffer))==SOCKET_ERROR)
-  {
-    return INADDR_ANY; //well take any address, if we fail
-  }
-
-  struct hostent *hosts=gethostbyname(buffer);
-  if (hosts==NULL)
-  {
-    return INADDR_ANY; //well take any address, if we fail
-  }
-
-  int num_ip=0;
-  for (num_ip=0;hosts->h_addr_list[num_ip]!=NULL;num_ip++);
-
-  int get_ip=(num%num_ip);//Just wrap around, if no interface are present any more
-  memcpy(&returnaddress, hosts->h_addr_list[get_ip], sizeof(ULONG));
-  return returnaddress;
-}
-#endif
diff --git a/dsock.h b/dsock.h
deleted file mode 100644 (file)
index 03fe7b4..0000000
--- a/dsock.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-    Copyright 2004-2020 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 DSOCK_H
-#define DSOCK_H
-
-#ifndef WIN32
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <sys/time.h>
-#define SOCKET_ERROR 0
-#else
-#include <winsock2.h>
-#include <Ws2tcpip.h>
-#include <sys/timeb.h>
-#endif
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include "defines.h"
-
-#define MAXBUFLEN 2000
-
-class DatagramSocket
-{
-  public:
-    DatagramSocket(short);             // port
-    ~DatagramSocket();
-    int init();
-    void shutdown();
-#ifdef WIN32
-    unsigned char waitforMessage(unsigned char, SOCKET quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
-#else
-    unsigned char waitforMessage(unsigned char, int quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
-#endif
-    UINT getDataLength() const;
-    const void* getData() const;         // returns a pointer to the data
-    const char* getFromIPA() const;      // returns a pointer to from IP address
-    short getFromPort() const;
-    void send(const char *, short, char *, int); // send wants: IP Address ddn style, port, data, length of data
-
-  private:
-    bool initted{};
-    ULONG getIPNumber(ULONG num);
-    ULONG iterate_ip{};
-    const static char DSOCKDEBUG = 0;
-    int socketnum;                  // Socket descriptor
-    short myPort;                   // My port number
-    struct sockaddr_in myAddr;      // My address
-    struct sockaddr_in theirAddr;   // User address
-    socklen_t addrlen;              // length of sockaddr struct
-    char buf[MAXBUFLEN];            // main data buffer
-    char fromIPA[20];               // from string (ip address)
-    short fromPort;                 // which port user sent on
-    int mlength;                    // length of message
-    struct timeval tv;
-    fd_set readfds;
-};
-
-#endif
index 5aab2f7507e100be7996746ac56858fb46a65e7e..25a81a1785c52a59a21edfac4f8d562683cbc93f 100644 (file)
@@ -59,6 +59,8 @@ bool InputMan::init()
   i1 = inputLinux->init();
   if (!i1) { delete inputLinux; inputLinux = NULL; }
 
+  // FIXME enable modules by new config system
+
 //  inputCEC = new InputCEC();
 //  i2 = inputCEC->init();
 //  if (!i2) { delete inputCEC; inputCEC = NULL; }
index 1834e1ade0dee56263374e86257ab505ac9c1089..6b26064fea77409d7316fd6742684b0c6a1aed30 100644 (file)
@@ -22,7 +22,6 @@
 #include <unistd.h>
 #endif
 
-#include "dsock.h"
 #include "log.h"
 
 #include "inputudp.h"
@@ -36,11 +35,9 @@ bool InputUDP::init()
   log = Log::getInstance();
   log->log("InputUDP", Log::DEBUG, "Starting InputUDP command server");
 
-  ds = new DatagramSocket(2000);
-  if (!ds->init())
+  if (!udp4.init(2000))
   {
-    log->log("InputUDP", Log::DEBUG, "DSock init error");
-    delete ds;
+    log->log("InputUDP", Log::DEBUG, "UDP4 init error");
     initted = false;
     return false;
   }
@@ -55,8 +52,7 @@ bool InputUDP::init()
   {
     Log::getInstance()->log("InputUDP", Log::ERR, "pipe2() fail");
 #endif
-    ds->shutdown();
-    delete ds;
+    udp4.shutdown();
     initted = false;
     return false;
   }
@@ -70,9 +66,7 @@ void InputUDP::shutdown()
   CLOSESOCKET(quitPipe);
 #endif
 
-
-  ds->shutdown();
-  delete ds;
+  udp4.shutdown();
 
 #ifndef WIN32
   CLOSESOCKET(pfds[1]);
@@ -122,15 +116,15 @@ void InputUDP::listenLoop()
   while(1)
   {
 #ifdef WIN32
-    retval = ds->waitforMessage(3, quitPipe);
+    retval = udp4.waitforMessage(3, quitPipe);
 #else
-    retval = ds->waitforMessage(3, pfds[0]);
+    retval = udp4.waitforMessage(3, pfds[0]);
 #endif
     Log::getInstance()->log("InputUDP", Log::DEBUG, "Back from waitForMessage");
 
     if (retval == 2)
     {
-      processRequest(ds->getData(), ds->getDataLength());
+      processRequest(udp4.getData(), udp4.getDataLength());
     }
     else if (retval == 3) // quit
     {
index 45a014673f7de59d462a0c1506b98b4b897eeb16..151227eeb3130253101f8feb69dbea4bbce7ac51 100644 (file)
@@ -29,9 +29,9 @@
 #endif
 
 #include "defines.h"
+#include "udp4.h"
 #include "input.h"
 
-class DatagramSocket;
 class Log;
 
 class InputUDP : public Input
@@ -54,7 +54,7 @@ class InputUDP : public Input
     const char* modName() { return myModName; }
 
     bool initted{};
-    DatagramSocket* ds{};
+    UDP4 udp4;
     Log* log{};
 
     std::thread listenThread;
index 8be322774422cdae21563d383b0789388e02b862..a7b6201beada29f45363ddc6d0394c16b2b00bfd 100644 (file)
@@ -1,4 +1,4 @@
-OBJ_COMMON = command.o tcp.o dsock.o thread.o timers.o i18n.o vdp6.o               \
+OBJ_COMMON = command.o thread.o timers.o i18n.o tcp.o udp4.o udp6.o vdpc.o         \
              message.o messagequeue.o wol.o audio.o video.o log.o                  \
              vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o     \
              directory.o mark.o option.o vfeed.o afeed.o                           \
diff --git a/udp4.cc b/udp4.cc
new file mode 100644 (file)
index 0000000..1382b7f
--- /dev/null
+++ b/udp4.cc
@@ -0,0 +1,217 @@
+/*
+    Copyright 2004-2020 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 WIN32
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#define SOCKET_ERROR 0
+#else
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <sys/timeb.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "defines.h"
+#include "log.h"
+
+#include "udp4.h"
+
+UDP4::~UDP4()
+{
+  if (initted) shutdown();
+}
+
+int UDP4::init(USHORT tport)
+{
+  if (initted) return 0;
+  
+  myPort = tport;
+  addrlen = sizeof(struct sockaddr);
+
+  if ((socketnum = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+  { perror("socket"); return 0; }
+
+  myAddr.sin_family = AF_INET;         // host byte order
+  myAddr.sin_port = htons(myPort);     // short, network byte order
+  myAddr.sin_addr.s_addr = getIPNumber(iterate_ip++); // auto-fill with my IP
+  memset(&(myAddr.sin_zero), 0, 8);    // zero the rest of the struct
+
+  if (bind(socketnum, reinterpret_cast<struct sockaddr *>(&myAddr), addrlen) == -1)
+  { perror("bind"); return 0; }
+
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  int allowed = 1;
+
+#ifdef WIN32
+  setsockopt(socketnum, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&allowed), sizeof(allowed));
+#else
+  setsockopt(socketnum, SOL_SOCKET, SO_BROADCAST, static_cast<void*>(&allowed), sizeof(allowed));
+#endif
+
+  initted = true;
+
+  return 1;
+}
+
+void UDP4::shutdown()
+{
+  if (!initted) return;
+  CLOSESOCKET(socketnum);
+  initted = false;
+}
+
+#ifdef WIN32
+unsigned char UDP4::waitforMessage(unsigned char how, SOCKET quitPipe)
+#else
+unsigned char UDP4::waitforMessage(unsigned char how, int quitPipe)
+#endif
+{
+  if (!initted) return 0;
+
+  /* how = 0 - block
+     how = 1 - start new wait
+     how = 2 - continue wait
+     how = 3 - block, return on byte from quitPipe
+  */
+
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+
+  struct timeval* passToSelect = NULL;
+
+  int sockMaxP1 = socketnum + 1;
+
+  if (how == 1)
+  {
+    tv.tv_sec = 1;
+    tv.tv_usec = 500000;
+    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 = 500000;
+    }
+    passToSelect = &tv;
+  }
+  else if (how == 3)
+  {
+    FD_SET(quitPipe, &readfds);
+    if (quitPipe > socketnum) sockMaxP1 = quitPipe + 1;
+  }
+
+
+  if (select(sockMaxP1, &readfds, NULL, NULL, passToSelect) <= 0)
+  {  return 1;  }
+
+  if ((how == 3) && FD_ISSET(quitPipe, &readfds)) return 3;
+
+  if ((mlength = recvfrom(socketnum, buf, MAXBUFLEN, 0,
+      reinterpret_cast<struct sockaddr *>(&theirAddr), &addrlen)) == -1)
+  { perror("recvfrom"); return 0; }
+  else
+  {
+    memset(&buf[mlength], 0, MAXBUFLEN - mlength);
+    strcpy(fromIPA, inet_ntoa(theirAddr.sin_addr));
+    fromPort = ntohs(theirAddr.sin_port);
+
+    return 2;
+  }
+
+  /* Return 0, failure
+     Return 1, nothing happened, timer expired
+     Return 2, packet arrived (timer not expired)
+  */
+}
+
+UINT UDP4::getDataLength(void) const
+{
+  return static_cast<UINT>(mlength);
+}
+
+const void* UDP4::getData() const     {  return buf;  }
+const char* UDP4::getFromIPA() const  {  return fromIPA;  }
+short UDP4::getFromPort() const       {  return fromPort; }
+
+bool UDP4::send(const char *ipa, USHORT port, char *message, int length)
+{
+  if (!initted) return false;
+
+  int sentLength = 0;
+
+  struct sockaddr_in sendAddr;
+  sendAddr.sin_family = AF_INET;      // host byte order
+  sendAddr.sin_port = htons(port);    // short, network byte order
+  struct in_addr tad;                 // temp struct tad needed to pass to sendAddr.sin_addr
+  tad.s_addr = inet_addr(ipa);
+  sendAddr.sin_addr = tad;            // address
+  memset(&(sendAddr.sin_zero), 0, 8); // zero the rest of the struct
+
+  errno = 0;
+
+  sentLength = sendto(socketnum, message, length, 0, reinterpret_cast<struct sockaddr *>(&sendAddr), addrlen);
+  if (sentLength == length) return true;
+  Log::getInstance()->log("UDP6", Log::ERR, "sendto failed, errno = %i", errno);
+  return false;
+}
+
+#ifndef WIN32
+ULONG UDP4::getIPNumber(ULONG)
+{
+  return INADDR_ANY;
+}
+#else
+ULONG UDP4::getIPNumber(ULONG num)
+{
+  char buffer[100];
+  ULONG returnaddress;
+
+  if (gethostname(buffer,sizeof(buffer))==SOCKET_ERROR)
+  {
+    return INADDR_ANY; //well take any address, if we fail
+  }
+
+  struct hostent *hosts=gethostbyname(buffer);
+  if (hosts==NULL)
+  {
+    return INADDR_ANY; //well take any address, if we fail
+  }
+
+  int num_ip=0;
+  for (num_ip=0;hosts->h_addr_list[num_ip]!=NULL;num_ip++);
+
+  int get_ip=(num%num_ip);//Just wrap around, if no interface are present any more
+  memcpy(&returnaddress, hosts->h_addr_list[get_ip], sizeof(ULONG));
+  return returnaddress;
+}
+#endif
diff --git a/udp4.h b/udp4.h
new file mode 100644 (file)
index 0000000..63e7da5
--- /dev/null
+++ b/udp4.h
@@ -0,0 +1,65 @@
+/*
+    Copyright 2004-2020 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 DSOCK_H
+#define DSOCK_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include "defines.h"
+
+#define MAXBUFLEN 2000
+
+class UDP4
+{
+  public:
+    ~UDP4();
+    int init(USHORT port);
+    void shutdown();
+#ifdef WIN32
+    unsigned char waitforMessage(unsigned char, SOCKET quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
+#else
+    unsigned char waitforMessage(unsigned char, int quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
+#endif
+    UINT getDataLength() const;
+    const void* getData() const;         // returns a pointer to the data
+    const char* getFromIPA() const;      // returns a pointer to from IP address
+    short getFromPort() const;
+    bool send(const char *, USHORT, char *, int); // send wants: IP Address ddn style, port, data, length of data
+
+  private:
+    bool initted{};
+    ULONG getIPNumber(ULONG num);
+    ULONG iterate_ip{};
+    int socketnum;                  // Socket descriptor
+    USHORT myPort{};                // My port number
+    struct sockaddr_in myAddr;      // My address
+    struct sockaddr_in theirAddr;   // User address
+    socklen_t addrlen;              // length of sockaddr struct
+    char buf[MAXBUFLEN];            // main data buffer
+    char fromIPA[20];               // from string (ip address)
+    short fromPort;                 // which port user sent on
+    int mlength;                    // length of message
+    struct timeval tv;
+    fd_set readfds;
+};
+
+#endif
diff --git a/udp6.cc b/udp6.cc
new file mode 100644 (file)
index 0000000..9e9e402
--- /dev/null
+++ b/udp6.cc
@@ -0,0 +1,236 @@
+/*
+    Copyright 2020 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 WIN32
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#define SOCKET_ERROR 0
+#else
+#include <winsock2.h>
+#include <Ws2tcpip.h>
+#include <sys/timeb.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <net/if.h>
+#include <fcntl.h>
+
+#include "log.h"
+
+#include "udp6.h"
+
+UDP6::~UDP6()
+{
+  if (initted) shutdown();
+}
+
+int UDP6::init(USHORT tPort)
+{
+  if (initted) return 0;
+  myPort = tPort;
+  addrlen = sizeof(struct sockaddr_in6);
+
+  if ((socketnum = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+  { perror("socket"); return 0; }
+
+  // FIXME - implement server side
+
+  /*
+
+  memset(&myAddr, 0, sizeof(myAddr));
+  myAddr.sin6_family = AF_INET6;        // host byte order
+  myAddr.sin6_port = htons(myPort);     // short, network byte order
+
+//  myAddr.sin_addr.s_addr = getIPNumber(iterate_ip++); // auto-fill with my IP
+
+  inet_pton(AF_INET6, "", &myAddr.sin6_addr);
+
+/ *
+  if (bind(socketnum, reinterpret_cast<struct sockaddr *>(&myAddr), addrlen) == -1)
+  { perror("bind"); return 0; }
+* /
+
+*/
+
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  initted = true;
+
+  return 1;
+}
+
+void UDP6::shutdown()
+{
+  if (!initted) return;
+  CLOSESOCKET(socketnum);
+  initted = false;
+}
+
+#ifdef WIN32
+unsigned char UDP6::waitforMessage(unsigned char how, SOCKET quitPipe)
+#else
+unsigned char UDP6::waitforMessage(unsigned char how, int quitPipe)
+#endif
+{
+  if (!initted) return 0;
+
+  /* how = 0 - block
+     how = 1 - start new wait
+     how = 2 - continue wait
+     how = 3 - block, return on byte from quitPipe
+  */
+
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+
+  struct timeval* passToSelect = NULL;
+
+  int sockMaxP1 = socketnum + 1;
+
+  if (how == 1)
+  {
+    tv.tv_sec = 1;
+    tv.tv_usec = 500000;
+    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 = 500000;
+    }
+    passToSelect = &tv;
+  }
+  else if (how == 3)
+  {
+    FD_SET(quitPipe, &readfds);
+    if (quitPipe > socketnum) sockMaxP1 = quitPipe + 1;
+  }
+
+
+  if (select(sockMaxP1, &readfds, NULL, NULL, passToSelect) <= 0)
+  {  return 1;  }
+
+  if ((how == 3) && FD_ISSET(quitPipe, &readfds)) return 3;
+
+  if ((mlength = recvfrom(socketnum, buf, MAXBUFLEN, 0,
+      reinterpret_cast<struct sockaddr *>(&theirAddr), &addrlen)) == -1)
+  { perror("recvfrom"); return 0; }
+  else
+  {
+    memset(&buf[mlength], 0, MAXBUFLEN - mlength);
+    inet_ntop(AF_INET6, &theirAddr.sin6_addr, fromIPA, 40);
+    fromPort = ntohs(theirAddr.sin6_port);
+    return 2;
+  }
+
+  /* Return 0, failure
+     Return 1, nothing happened, timer expired
+     Return 2, packet arrived (timer not expired)
+  */
+}
+
+UINT UDP6::getDataLength(void) const
+{
+  return static_cast<UINT>(mlength);
+}
+
+const void* UDP6::getData() const     {  return buf;  }
+const char* UDP6::getFromIPA() const  {  return fromIPA;  }
+short UDP6::getFromPort() const       {  return fromPort; }
+
+bool UDP6::send(const char *ipa, USHORT port, char *message, int length, bool mcast)
+{
+  if (!initted) return false;
+  int sentLength = 0;
+  errno = 0;
+
+  struct sockaddr_in6 sendAddr;
+  memset(&sendAddr, 0, sizeof(struct sockaddr_in6));
+  sendAddr.sin6_family = AF_INET6;
+  inet_pton(AF_INET6, ipa, &sendAddr.sin6_addr);
+  sendAddr.sin6_port = htons(port);
+
+  if (mcast)
+  {
+    struct if_nameindex* ifs = if_nameindex();
+    UINT ifIndex;
+
+    for(int i = 0; ifs[i].if_index > 0; i++)
+    {
+      ifIndex = ifs[i].if_index;
+      int res = setsockopt(socketnum, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, sizeof(ifIndex));
+      Log::getInstance()->log("UDP6", Log::DEBUG, "Transmitting IPv6 MC UDP on %s", ifs[i].if_name);
+      res = sendto(socketnum, message, length, 0, reinterpret_cast<struct sockaddr *>(&sendAddr), addrlen);
+      Log::getInstance()->log("UDP6", Log::DEBUG, "Result: %i. Errno: %i", res, errno);
+      errno = 0;
+    }
+
+    if_freenameindex(ifs);
+  }
+  else
+  {
+    sentLength = sendto(socketnum, message, length, 0, reinterpret_cast<struct sockaddr *>(&sendAddr), addrlen);
+    if (sentLength == length) return true;
+    Log::getInstance()->log("UDP6", Log::ERR, "sendto failed, errno = %i", errno);
+  }
+
+  return false;
+}
+
+#ifndef WIN32
+ULONG UDP6::getIPNumber(ULONG)
+{
+  return INADDR_ANY;
+}
+#else
+ULONG UDP6::getIPNumber(ULONG num)
+{
+  char buffer[100];
+  ULONG returnaddress;
+
+  if (gethostname(buffer,sizeof(buffer))==SOCKET_ERROR)
+  {
+    return INADDR_ANY; //well take any address, if we fail
+  }
+
+  struct hostent *hosts=gethostbyname(buffer);
+  if (hosts==NULL)
+  {
+    return INADDR_ANY; //well take any address, if we fail
+  }
+
+  int num_ip=0;
+  for (num_ip=0;hosts->h_addr_list[num_ip]!=NULL;num_ip++);
+
+  int get_ip=(num%num_ip);//Just wrap around, if no interface are present any more
+  memcpy(&returnaddress, hosts->h_addr_list[get_ip], sizeof(ULONG));
+  return returnaddress;
+}
+#endif
diff --git a/udp6.h b/udp6.h
new file mode 100644 (file)
index 0000000..bebd3bf
--- /dev/null
+++ b/udp6.h
@@ -0,0 +1,64 @@
+/*
+    Copyright 2020 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 UDP6_H
+#define UDP6_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "defines.h"
+
+#define MAXBUFLEN 2000
+
+class UDP6
+{
+  public:
+    ~UDP6();
+    int init(USHORT port);
+    void shutdown();
+#ifdef WIN32
+    unsigned char waitforMessage(unsigned char, SOCKET quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
+#else
+    unsigned char waitforMessage(unsigned char, int quitPipe = 0); // uchar =0-block =1-new wait =2-continue wait
+#endif
+    UINT getDataLength() const;
+    const void* getData() const;         // returns a pointer to the data
+    const char* getFromIPA() const;      // returns a pointer to from IP address
+    short getFromPort() const;
+    bool send(const char *, USHORT, char *, int, bool mcast = false); // send wants: text IP, port, data, length of data, is_mcast
+
+  private:
+    bool initted{};
+    ULONG getIPNumber(ULONG num);
+    ULONG iterate_ip{};
+    int socketnum;                  // Socket descriptor
+    USHORT myPort{};                // My port number
+//    struct sockaddr_in6 myAddr;     // My address
+    struct sockaddr_in6 theirAddr;  // User address
+    socklen_t addrlen;              // length of sockaddr struct
+    char buf[MAXBUFLEN];            // main data buffer
+    char fromIPA[40];               // from string (ip address)
+    short fromPort;                 // which port user sent on
+    int mlength;                    // length of message
+    struct timeval tv;
+    fd_set readfds;
+};
+
+#endif
index c31cb09b6bf791d87821153fff72482c50ee3bea..1d31768356e88ab7a058f0e7dbf383bc1619397d 100644 (file)
@@ -25,6 +25,7 @@
 #include "boxstack.h"
 #include "message.h"
 #include "log.h"
+#include "vdr.h"
 #include "wol.h"
 #include "vserverselect.h"
 #include "messagequeue.h"
@@ -54,7 +55,7 @@ VConnect::VConnect()
 VConnect::~VConnect()
 {
   threadReqQuit = true;
-  vdr->cancelFindingServer();
+  vdpc.stop();
   stop();
 }
 
@@ -100,29 +101,40 @@ void VConnect::threadMethod()
 
   const std::string& commandLineServer = getCommandLineServer();
 
+  if (!vdpc.init())
+  {
+    logger->log("VConnect", Log::CRIT, "Failed to init VDPC");
+    return;
+  }
+
   do
   {
     if (!commandLineServer.empty()) // Server is specified, fake a servers array
     {
-      servers.emplace_back(commandLineServer, "", 3024, 0);
+      //servers.emplace_back(commandLineServer, "", 3024, 0);
+
+      vdpc.TEMPaddCLIServer(commandLineServer); // FIXME move to new config system NCONFIG
     }
     else
     {
       setOneLiner(tr("Locating server"));
       draw();
       boxstack->update(this);
-      vdr->findServers(servers);
+      vdpc.go();
       if (threadReqQuit) return;
     }
 
-    if (servers.size() == 1)
+    for (ULONG i = 0; i < vdpc.numServers(); i++)
+      logger->log("VConnect", Log::INFO, "Found server: %i %s %s %u", vdpc[i].ipVersion, vdpc[i].ip.c_str(), vdpc[i].name.c_str(), vdpc[i].port, vdpc[i].version);
+
+    if (vdpc.numServers() == 1)
     {
       selectedServer = 0;
     }
     else
     {
       selectedServer = -1;
-      VServerSelect* vs = new VServerSelect(servers, this);
+      VServerSelect* vs = new VServerSelect(vdpc, this);
       vs->draw();
       boxstack->add(vs);
       boxstack->update(vs);
@@ -135,10 +147,10 @@ void VConnect::threadMethod()
 
     if (threadReqQuit) return;
 
-    logger->log("VConnect", Log::NOTICE, "Connecting to server at %s %u", servers[selectedServer].ip.c_str(), servers[selectedServer].port);
-    Wol::getInstance()->setWakeUpIP(servers[selectedServer].ip.c_str());
-    vdr->setServerIP(servers[selectedServer].ip.c_str());
-    vdr->setServerPort(servers[selectedServer].port);
+    logger->log("VConnect", Log::NOTICE, "Connecting to server at %s %u", vdpc[selectedServer].ip.c_str(), vdpc[selectedServer].port);
+    Wol::getInstance()->setWakeUpIP(vdpc[selectedServer].ip.c_str());
+    vdr->setServerIP(vdpc[selectedServer].ip.c_str());
+    vdr->setServerPort(vdpc[selectedServer].port);
 
     setOneLiner(tr("Connecting to VDR"));
     draw();
@@ -175,9 +187,6 @@ void VConnect::threadMethod()
     draw();
     boxstack->update(this);
     MILLISLEEP(delay);
-
-    servers.clear(); // In case we're going around
-
   } while(!success);
 
   logger->log("VConnect", Log::INFO, "Send VDR connected message");
index 3ea21ce06ed8adc806c3bc8d792479ca83f1e217..25580291c9358cbc05cd2316c208fa4a4ee2c33d 100644 (file)
 #ifndef VCONNECT_H
 #define VCONNECT_H
 
-#include <vector>
 #include <mutex>
 #include <thread>
 #include <condition_variable>
 
 #include "vinfo.h"
-#include "vdr.h"
+#include "vdpc.h"
 
 class Log;
 class BoxStack;
@@ -45,13 +44,11 @@ class VConnect : public VInfo
     void run();
 
   private:
-
-    void clearServerIPs();
-
     BoxStack* boxstack;
-    VDR* vdr;
     Log* logger;
-    std::vector<VDRServer> servers;
+    VDPC vdpc;
+    VDR* vdr;
+
     int selectedServer;
 
     std::thread connectThread;
diff --git a/vdp6.cc b/vdp6.cc
deleted file mode 100644 (file)
index 0dfa21f..0000000
--- a/vdp6.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-    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/>.
-*/
-
-#if IPV6
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "vdr.h"
-#include "log.h"
-#include "vdp6.h"
-
-VDP6::VDP6()
-{
-}
-
-void VDP6::run()
-{
-  Log* logger = Log::getInstance();
-
-  if (pipe2(pfds, O_NONBLOCK) == -1)
-  {
-    logger->log("VDP6", Log::ERR, "pipe2 error");
-    return;
-  }
-  
-  sock = socket(AF_INET6, SOCK_DGRAM, 0);
-  if (sock < 0)
-  {
-    logger->log("VDP6", Log::ERR, "socket error");
-    return;
-  }
-
-  struct sockaddr_in6 saddr;
-  memset(&saddr, 0, sizeof(saddr));
-  saddr.sin6_family = AF_INET6;
-  inet_pton(AF_INET6, "ff15:766f:6d70:2064:6973:636f:7665:7279", &saddr.sin6_addr);
-  saddr.sin6_port = htons(51056);
-
-  receiveThread = std::thread( [this, logger]
-  {
-    socklen_t addrlen = sizeof(struct sockaddr_in6);
-    char vdpreply[1000];
-    fd_set readfds;
-    struct timeval timeout;
-    timeout.tv_sec = 1;
-    timeout.tv_usec = 500000;
-
-    while(1)
-    {
-      FD_ZERO(&readfds);
-      FD_SET(sock, &readfds);
-      FD_SET(pfds[0], &readfds);
-
-      int sresult = select(((sock > pfds[0]) ? sock : pfds[0]) + 1, &readfds, NULL, NULL, &timeout);
-      if (sresult < 1) break;
-
-      if (FD_ISSET(pfds[0], &readfds)) break;                        
-                              
-      int mlength;
-      struct sockaddr_in6 theirAddr;
-      mlength = recvfrom(sock, vdpreply, 1000, 0, reinterpret_cast<struct sockaddr *>(&theirAddr), &addrlen);
-      if (mlength == -1)
-      {
-        logger->log("VDP6", Log::ERR, "recvfrom error");
-        break;
-      }
-
-      // FIXME upgrade this to look for a return IP in the reply packet
-      char tempStringIP[40];
-      inet_ntop(AF_INET6, &theirAddr.sin6_addr, tempStringIP, sizeof(theirAddr));
-
-      USHORT tempPort;
-      memcpy(&tempPort, &vdpreply[26], 2);
-
-      ULONG newServerVersion;
-      memcpy(&newServerVersion, &vdpreply[28], 4);
-
-      //logger->log("VDP6", Log::INFO, "Got response: %s %u %s %lx", newServer.ip, newServer.port, newServer.name, newServer.version);
-      servers.emplace_back(tempStringIP, &vdpreply[32], ntohs(tempPort), ntohl(newServerVersion));
-    }
-  });
-
-  char message[15];
-  memset(message, 0, 15);
-  strcpy(message, "VDP-0001");
-
-  struct if_nameindex* ifs = if_nameindex();
-  UINT ifIndex;
-
-  for(int i = 0; ifs[i].if_index > 0; i++)
-  {
-    ifIndex = ifs[i].if_index;
-    int d = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, sizeof(ifIndex));
-    d = sendto(sock, message, 15, 0, reinterpret_cast<struct sockaddr *>(&saddr), sizeof(saddr));
-    if (d > 0) logger->log("VDP6", Log::DEBUG, "Transmitted IPv6 MC UDP on %s", ifs[i].if_name);
-  }
-
-  if_freenameindex(ifs);
-}
-
-UINT VDP6::numFound()
-{
-  return servers.size();
-}
-
-void VDP6::stop()
-{
-  write(pfds[1], "X", 1);
-  receiveThread.join();
-  
-  close(pfds[0]);
-  close(pfds[1]);
-  close(sock);
-}
-
-#endif
diff --git a/vdp6.h b/vdp6.h
deleted file mode 100644 (file)
index d213dea..0000000
--- a/vdp6.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-    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 VDP_H
-#define VDP_H
-
-#if IPV6
-
-#include <thread>
-#include <vector>
-
-#include "defines.h"
-
-struct VDRServer;
-
-/*
- * Until VDP is reorganised, getServers must be called after stop
- * and the buffers pointed to by the VDRServer structs must be freed
- * by the caller.
- */
-
-class VDP6
-{
-  public:
-    VDP6();
-
-    void run();
-    void stop();
-    UINT numFound();
-    std::vector<VDRServer>* getServers() { return &servers; }
-
-  private:
-    int pfds[2];
-    int sock;
-    std::thread receiveThread;
-    std::vector<VDRServer> servers;
-};
-
-#endif
-
-
-
-#endif
diff --git a/vdpc.cc b/vdpc.cc
new file mode 100644 (file)
index 0000000..b2adead
--- /dev/null
+++ b/vdpc.cc
@@ -0,0 +1,344 @@
+/*
+    Copyright 2020 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 <unistd.h>
+#include <fcntl.h>
+
+#include <cstdlib>
+#include <chrono>
+#include <algorithm>
+
+#include "log.h"
+#include "wol.h"
+
+#include "vdpc.h"
+
+void VDPC::TEMPaddCLIServer(const std::string& cliServer)
+{
+  std::lock_guard<std::mutex> lg(serversLock);
+  servers.emplace_back(4, cliServer, "", 3024, 0);
+}
+
+bool VDPC::init()
+{
+  AddServerCallback addFunc( [this] (int ipVersion, const char* ip, const char* name, USHORT port, ULONG version)
+  {
+    std::lock_guard<std::mutex> lg(serversLock);
+
+    if (dedupServers)
+    {
+      UINT i = 0;
+      for (; i < servers.size(); i++)
+      {
+        if (servers[i].name == name) break;
+      }
+
+      if (i == servers.size())
+      {
+        servers.emplace_back(ipVersion, ip, name, port, version);
+      }
+      else
+      {
+        if (ipVersion == preferIPV) // delete the other
+        {
+          servers[i].ipVersion = ipVersion;
+          servers[i].ip = ip;
+          servers[i].name = name;
+          servers[i].port = port;
+          servers[i].version = version;
+        }
+      }
+
+    }
+    else
+    {
+      servers.emplace_back(ipVersion, ip, name, port, version);
+    }
+  });
+
+#ifdef IPV4
+  if (!vdpc4.init(addFunc)) return false;
+#endif
+#ifdef IPV6
+  if (!vdpc6.init(addFunc)) return false;
+#endif
+
+  return true;
+}
+
+int VDPC::go()
+{
+  std::unique_lock<std::mutex> ul(waitMutex);
+
+  servers.clear();
+
+#ifdef IPV4
+  vdpc4.run();
+#endif
+#ifdef IPV6
+  vdpc6.run();
+#endif
+
+  do
+  {
+    Log::getInstance()->log("VDPC", Log::DEBUG, "Sending broadcasts");
+
+#ifdef IPV4
+    vdpc4.sendRequest();
+#endif
+#ifdef IPV6
+    vdpc6.sendRequest();
+#endif
+
+    waitCond.wait_for(ul, std::chrono::milliseconds(1500), [this]{ return stopNow == true; });
+    Log::getInstance()->log("VDPC", Log::DEBUG, "go() wait finished");
+
+    if (stopNow) break;
+    if (servers.size() == 0) Wol::getInstance()->doWakeUp();
+
+  } while(servers.size() < 1);
+
+#ifdef IPV4
+    vdpc4.stop();
+#endif
+#ifdef IPV6
+    vdpc6.stop();
+#endif
+
+  std::sort(servers.begin(), servers.end(), ServerSorter(preferIPV));
+  return servers.size();
+}
+
+void VDPC::stop()
+{
+  std::lock_guard<std::mutex> lg(waitMutex);
+  stopNow = true;
+  waitCond.notify_one();
+}
+
+ULONG VDPC::numServers() const
+{
+  return servers.size();
+}
+
+const VDRServer& VDPC::operator[](ULONG index) const
+{
+  if (index >= servers.size()) std::abort();
+  return servers[index];
+}
+
+#ifdef IPV4
+
+// ======================================= VDPC4
+
+bool VDPC::VDPC4::init(AddServerCallback& taddFunc)
+{
+  Log::getInstance()->log("VDPC4", Log::DEBUG, "init");
+  addFunc = taddFunc;
+
+  if (!udp4.init(0))
+  {
+    Log::getInstance()->log("VDPC4", Log::CRIT, "Failed to init VDPC4 UDP");
+    return false;
+  }
+
+  return true;
+}
+
+void VDPC::VDPC4::run()
+{
+  Log::getInstance()->log("VDPC4", Log::DEBUG, "run");
+  stopNow = false;
+
+  startProtect.lock();
+  receiveThread = std::thread([this]
+  {
+    startProtect.lock();
+    startProtect.unlock();
+    threadMethod();
+  });
+  startProtect.unlock();
+}
+
+void VDPC::VDPC4::stop()
+{
+  Log::getInstance()->log("VDPC4", Log::DEBUG, "stop");
+
+  stopNow = true;
+  write(pfdsUDP4[1], "X", 1);
+  receiveThread.join();
+
+  close(pfdsUDP4[0]);
+  close(pfdsUDP4[1]);
+}
+
+void VDPC::VDPC4::threadMethod()
+{
+  Log* logger = Log::getInstance();
+
+  if (pipe2(pfdsUDP4, O_NONBLOCK) == -1)
+  {
+    logger->log("VDPC4", Log::ERR, "pipe2 error B");
+    return;
+  }
+
+  while (!stopNow)
+  {
+    Log::getInstance()->log("VDPC4", Log::DEBUG, "goto waitformessage");
+
+    UCHAR retval = udp4.waitforMessage(3, pfdsUDP4[0]);
+    Log::getInstance()->log("VDPC4", Log::DEBUG, "backfrom waitformessage");
+
+    if (retval == 2) // we got a reply
+    {
+      const char* vdpreply = static_cast<const char*>(udp4.getData());
+      if ((udp4.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
+      {
+        // FIXME upgrade this to look for a return IP in the reply packet
+
+        USHORT newServerPort;
+        memcpy(&newServerPort, &vdpreply[26], 2);
+
+        ULONG newServerVersion;
+        memcpy(&newServerVersion, &vdpreply[28], 4);
+
+        // FIXME - packet length > 24 not checked, end NULL not checked!!
+        addFunc(4, udp4.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
+      }
+    }
+
+    Log::getInstance()->log("VDPC4", Log::DEBUG, "loop stopnow = %i", stopNow);
+  }
+}
+
+void VDPC::VDPC4::sendRequest()
+{
+  Log::getInstance()->log("VDPC4", Log::DEBUG, "broadcast");
+
+  char message[15];
+  memset(message, 0, 15);
+  strcpy(message, "VDP-0001");
+  /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
+
+  udp4.send("255.255.255.255", 51051U, message, 15);
+  udp4.send("255.255.255.255", 51052U, message, 15);
+  udp4.send("255.255.255.255", 51053U, message, 15);
+  udp4.send("255.255.255.255", 51054U, message, 15);
+  udp4.send("255.255.255.255", 51055U, message, 15);
+}
+
+#endif
+
+#ifdef IPV6
+
+// ======================================= VDPC6
+
+bool VDPC::VDPC6::init(AddServerCallback& taddFunc)
+{
+  Log::getInstance()->log("VDPC6", Log::DEBUG, "init");
+  addFunc = taddFunc;
+
+  if (!udp6.init(0))
+  {
+    Log::getInstance()->log("VDPC6", Log::CRIT, "Failed to init VDPC6 UDP");
+    return false;
+  }
+
+  return true;
+}
+
+void VDPC::VDPC6::run()
+{
+  Log::getInstance()->log("VDPC6", Log::DEBUG, "run");
+  stopNow = false;
+
+  startProtect.lock();
+  receiveThread = std::thread([this]
+  {
+    startProtect.lock();
+    startProtect.unlock();
+    threadMethod();
+  });
+  startProtect.unlock();
+}
+
+void VDPC::VDPC6::stop()
+{
+  Log::getInstance()->log("VDPC6", Log::DEBUG, "stop");
+
+  stopNow = true;
+  write(pfdsUDP6[1], "X", 1);
+  receiveThread.join();
+
+  close(pfdsUDP6[0]);
+  close(pfdsUDP6[1]);
+}
+
+void VDPC::VDPC6::threadMethod()
+{
+  Log* logger = Log::getInstance();
+
+  if (pipe2(pfdsUDP6, O_NONBLOCK) == -1)
+  {
+    logger->log("VDPC6", Log::ERR, "pipe2 error B");
+    return;
+  }
+
+  while (!stopNow)
+  {
+    Log::getInstance()->log("VDPC6", Log::DEBUG, "goto waitformessage");
+
+    UCHAR retval = udp6.waitforMessage(3, pfdsUDP6[0]);
+    Log::getInstance()->log("VDPC6", Log::DEBUG, "backfrom waitformessage");
+
+    if (retval == 2) // we got a reply
+    {
+      const char* vdpreply = static_cast<const char*>(udp6.getData());
+      if ((udp6.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
+      {
+        // FIXME upgrade this to look for a return IP in the reply packet
+
+        USHORT newServerPort;
+        memcpy(&newServerPort, &vdpreply[26], 2);
+
+        ULONG newServerVersion;
+        memcpy(&newServerVersion, &vdpreply[28], 4);
+
+        // FIXME - packet length > 24 not checked, end NULL not checked!!
+        addFunc(6, udp6.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
+      }
+    }
+
+    Log::getInstance()->log("VDPC6", Log::DEBUG, "loop stopnow = %i", stopNow);
+  }
+}
+
+void VDPC::VDPC6::sendRequest()
+{
+  Log::getInstance()->log("VDPC6", Log::DEBUG, "broadcast");
+
+  char message[15];
+  memset(message, 0, 15);
+  strcpy(message, "VDP-0001");
+  /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
+
+  udp6.send("ff15:766f:6d70:2064:6973:636f:7665:7279", 51056U, message, 15, true);
+}
+
+#endif
diff --git a/vdpc.h b/vdpc.h
new file mode 100644 (file)
index 0000000..e196302
--- /dev/null
+++ b/vdpc.h
@@ -0,0 +1,147 @@
+/*
+    Copyright 2020 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 VDPC_H
+#define VDPC_H
+
+// Vomp Discovery Protocol Client
+
+#include <string>
+#include <vector>
+#include <mutex>
+#include <thread>
+#include <condition_variable>
+#include <functional>
+
+#include "defines.h"
+
+#ifdef IPV4
+#include "udp4.h"
+#endif
+#ifdef IPV6
+#include "udp6.h"
+#endif
+
+struct VDRServer
+{
+  int ipVersion;
+  std::string ip;
+  std::string name;
+  USHORT port;
+  ULONG version;
+
+  VDRServer(int tipVersion, const std::string tip, const std::string tname, USHORT tport, ULONG tversion)
+  : ipVersion(tipVersion), ip(tip), name(tname), port(tport), version(tversion) {}
+};
+
+class ServerSorter
+{
+  public:
+    ServerSorter(int tipPreferred) : ipPreferred(tipPreferred) {}
+    bool operator() (const VDRServer& a, const VDRServer& b)
+    {
+      if (a.name == b.name)
+        return a.ipVersion == ipPreferred;
+      else
+        return a.name < b.name;
+    }
+  private:
+    int ipPreferred;
+};
+
+class VDPC
+{
+  private:
+    typedef std::function<void(int, const char*, const char*, USHORT, ULONG)> AddServerCallback;
+
+  // Nested classes
+#ifdef IPV4
+    class VDPC4
+    {
+      public:
+        bool init(AddServerCallback& taddFunc);
+        void run(); // blocks
+        void stop();
+        void sendRequest();
+
+      private:
+        int pfdsUDP4[2];
+
+        std::mutex startProtect;
+        std::thread receiveThread;
+        bool stopNow{};
+        void threadMethod();
+
+        UDP4 udp4;
+        AddServerCallback addFunc;
+    };
+#endif
+
+#ifdef IPV6
+    class VDPC6
+    {
+      public:
+        bool init(AddServerCallback& taddFunc);
+        void run(); // blocks
+        void stop();
+        void sendRequest();
+
+      private:
+        int pfdsUDP6[2];
+
+        std::mutex startProtect;
+        std::thread receiveThread;
+        bool stopNow{};
+        void threadMethod();
+
+        UDP6 udp6;
+        AddServerCallback addFunc;
+    };
+#endif
+
+  public:
+    bool init();
+    int go();
+    void stop();
+    ULONG numServers() const;
+    const VDRServer& operator[](ULONG index) const;
+
+    void TEMPaddCLIServer(const std::string&); // FIXME NCONFIG
+
+  private:
+    std::vector<VDRServer> servers;
+    std::mutex serversLock;
+
+    std::condition_variable waitCond;
+    std::mutex waitMutex;
+    bool stopNow{};
+
+    // FIXME retrieve from new config system NCONFIG
+    bool dedupServers{true};
+    int preferIPV{4};
+
+#ifdef IPV4
+    VDPC4 vdpc4;
+#endif
+#ifdef IPV6
+    VDPC6 vdpc6;
+#endif
+};
+
+#endif
diff --git a/vdr.cc b/vdr.cc
index db620a4698293f882ab4c1abe7e81e5ff2f9ebef..958ef6fd68abb8f9a5462fa6f0ce7759032da24c 100644 (file)
--- a/vdr.cc
+++ b/vdr.cc
 #include "tcp.h"
 #include "log.h"
 #include "recinfo.h"
-#include "dsock.h"
 #include "channel.h"
 #include "event.h"
 #include "wol.h"
 #include "vdrrequestpacket.h"
 #include "vdrresponsepacket.h"
 #include "command.h"
-#include "vdp6.h"
 #ifdef VOMP_MEDIAPLAYER
 #include "media.h"
 #include "mediaprovider.h"
@@ -92,8 +90,6 @@ SerializeBuffer * VDR::doRequestResponse(SerializeBuffer *rq,int cmd) {
   return buf;
 }
 
-
-
 //deserialize a received response
 //delete the package
 //return !=0 on error
@@ -155,107 +151,6 @@ int VDR::shutdown()
   return 1;
 }
 
-void VDR::findServers(std::vector<VDRServer>& servers)
-{
-  Wol* wol = Wol::getInstance();
-  findingServer = 1;
-  char message[15];
-  memset(message, 0, 15);
-  strcpy(message, "VDP-0001");
-  /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
-
-#if IPV6
-  // FIXME Horrible hack. Rewrite all this.
-  // Change away from relying on the dsock select to do the timing
-  // Do timing here and let UDP4 / UDP6 objects/threads just wait
-  // for responses
-  VDP6 vdp6;
-  vdp6.run();
-#endif
-
-  DatagramSocket ds(0);
-  int haveAtLeastOne = 0;
-  int retval;
-  UCHAR waitType = 1;
-  bool firstloop = true;
-  while(findingServer)
-  {
-    if (waitType == 1)
-    {
-      ds.shutdown();
-      ds.init();
-      logger->log("VDR", Log::NOTICE, "Broadcasting for server");
-      ds.send("255.255.255.255", 51051U, message, 15);
-      ds.send("255.255.255.255", 51052U, message, 15);
-      ds.send("255.255.255.255", 51053U, message, 15);
-      ds.send("255.255.255.255", 51054U, message, 15);
-      ds.send("255.255.255.255", 51055U, message, 15);
-      if(!firstloop) wol->doWakeUp();
-    }
-    retval = ds.waitforMessage(waitType);
-
-    if (retval == 2) // we got a reply
-    {
-      waitType = 2;
-      
-      const char* vdpreply = static_cast<const char*>(ds.getData());
-      if ((ds.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
-      {
-        // FIXME upgrade this to look for a return IP in the reply packet
-
-        USHORT newServerPort;
-        memcpy(&newServerPort, &vdpreply[26], 2);
-
-        ULONG newServerVersion;
-        memcpy(&newServerVersion, &vdpreply[28], 4);
-        haveAtLeastOne = 1;
-
-        // FIXME - packet length > 24 not checked, end NULL not checked!!
-        // FIXME mutex protection ?? Nope this is a separate vector currently
-        servers.emplace_back(ds.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
-      }
-    }
-    else
-    {
-      if (haveAtLeastOne) break;
-#if IPV6
-      if (vdp6.numFound()) break;
-#endif
-
-      waitType = 1;
-      firstloop = false;
-    }
-  }
-  logger->log("VDR", Log::NOTICE, "END loop");
-
-#if IPV6
-  vdp6.stop();
-  std::vector<VDRServer>* servers6 = vdp6.getServers();
-  
-  // Add IPv6 found servers to servers vector, if not in servers already
-
-  for(auto i6 = servers6->begin(); i6 != servers6->end(); i6++)
-  {
-    bool found = false;
-
-    for(auto i4 = servers.begin(); i4 != servers.end(); i4++)
-      if (i4->name == i6->name) { found = true; break; }
-
-    if (found) continue; // Don't add
-
-    servers.push_back(std::move(*i6)); // VDP6 is stopped, the vector owner only goes out of scope after this method
-  }
-
-#endif
-
-  sort(servers.begin(), servers.end(), ServerSorter());
-}
-
-void VDR::cancelFindingServer()
-{
-  findingServer = 0;
-}
-
 void VDR::setServerIP(const char* newIP)
 {
   strcpy(serverIP, newIP);
diff --git a/vdr.h b/vdr.h
index 27ea09bfea5b036ae033f77959a6e0bacf7ac646..6e4dd308cc78bcde41a2aaac96a2ff78682c0303 100644 (file)
--- a/vdr.h
+++ b/vdr.h
@@ -61,17 +61,6 @@ typedef std::vector<Event*> EventList;
 typedef std::vector<Channel*> ChannelList;
 typedef std::vector<RecTimer*> RecTimerList;
 
-struct VDRServer
-{
-  std::string ip;
-  std::string name;
-  USHORT port;
-  ULONG version;
-
-  VDRServer(const std::string tip, const std::string tname, USHORT tport, ULONG tversion)
-  : ip(tip), name(tname), port(tport), version(tversion) {}
-};
-
 struct RecTimerSorter     // : public binary_function<double, double, bool>
 {
   bool operator() (const RecTimer* a, const RecTimer* b)
@@ -80,14 +69,6 @@ struct RecTimerSorter     // : public binary_function<double, double, bool>
   }
 };
 
-struct ServerSorter
-{
-  bool operator() (const VDRServer& a, const VDRServer& b)
-  {
-    return a.name < b.name;
-  }
-};
-
 class StreamReceiver
 {
   public:
@@ -140,8 +121,11 @@ public ExternLogger
     int init();
     int shutdown();
 
+    /*
     void findServers(std::vector<VDRServer>& servers);
     void cancelFindingServer();
+    */
+
     void setServerIP(const char*);
     void setServerPort(USHORT);
     void setReceiveWindow(size_t size);
index 09da0a33ffb6e2d573d0a159fbaa1abaf754a86e..767a556278304fe38233b8721bf56b2adb3b42ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2005 Chris Tallon
+    Copyright 2004-2020 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 "vserverselect.h"
-
 #include "defines.h"
 #include "input.h"
 #include "colour.h"
 #include "i18n.h"
 #include "messagequeue.h"
 
+#include "vserverselect.h"
 
-VServerSelect::VServerSelect(std::vector<VDRServer>& servers, void* treplyTo)
+VServerSelect::VServerSelect(const VDPC& servers, void* treplyTo)
 {
-  // I tried the whole passing using a reference here, but
-  // the program segfaulted when settitletext tried to new
-  // a char array. so now we have the messy dereferencing...
-  // anyway, now it doesn't use a object wide reference.
-
   setSize(300, 200);
   createBuffer();
   if (Video::getInstance()->getFormat() == Video::PAL)
@@ -56,7 +49,7 @@ VServerSelect::VServerSelect(std::vector<VDRServer>& servers, void* treplyTo)
   add(&sl);
 
   sl.addOption(servers[0].name.c_str(), 0, 1);
-  for(UINT k = 1; k < servers.size(); k++)
+  for(UINT k = 1; k < servers.numServers(); k++)
   {
     sl.addOption(servers[k].name.c_str(), 0, 0);
   }
index 2a4a022b113668c4a96a9e8ba57b9a400d5361b1..650a751e9b6b351d206b5e8ae980dff47916d101 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2005 Chris Tallon
+    Copyright 2004-2020 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/>.
 */
 
 #ifndef VSERVERSELECT_H
 #define VSERVERSELECT_H
 
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-
 #include "tbboxx.h"
-#include "vdr.h"
+#include "vdpc.h"
 #include "wselectlist.h"
 
 class Message;
@@ -34,7 +29,7 @@ class Message;
 class VServerSelect : public TBBoxx
 {
   public:
-    VServerSelect(std::vector<VDRServer>& servers, void* replyTo);
+    VServerSelect(const VDPC&, void* replyTo);
     ~VServerSelect();
 
     int handleCommand(int command);