]> git.vomp.tv Git - vompclient.git/commitdiff
Replace TCP. Use new IP API only, getMAC works with if!=eth0
authorChris Tallon <chris@vomp.tv>
Thu, 2 Apr 2020 16:09:02 +0000 (17:09 +0100)
committerChris Tallon <chris@vomp.tv>
Thu, 2 Apr 2020 16:09:02 +0000 (17:09 +0100)
objects.mk
tcp.cc [new file with mode: 0644]
tcp.h [new file with mode: 0644]
vdr.cc
vdr.h

index 35be1e2ef58c682141cd4a0a20f858831cafdea3..ee66ea4827586acb2d619492ae7163ad3d16d915 100644 (file)
@@ -1,4 +1,4 @@
-OBJ_COMMON = command.o thread.o timers.o i18n.o tcpold.o udp4.o udp6.o vdpc.o      \
+OBJ_COMMON = command.o thread.o timers.o i18n.o udp4.o udp6.o vdpc.o tcp.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/tcp.cc b/tcp.cc
new file mode 100644 (file)
index 0000000..7ab755b
--- /dev/null
+++ b/tcp.cc
@@ -0,0 +1,291 @@
+/*
+    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 <string.h> // memcmp
+#include <unistd.h> // pipe2
+#include <fcntl.h> // pipe2-O_NONBLOCK fcntl
+#include <sys/types.h>  // socket connect getaddrinfo
+#include <sys/socket.h> // socket connect getaddrinfo
+#include <string.h> // memset
+#include <netdb.h> // getaddrinfo
+#include <sys/select.h> // select
+#include <errno.h> // errno var
+#include <ifaddrs.h> // getifaddrs
+#include <linux/if_packet.h> // for getMAC
+#include <net/ethernet.h> // for getMAC
+
+#include "log.h"
+
+#include "tcp.h"
+
+TCP::~TCP()
+{
+  shutdown();
+  
+  if (abortPipe[0] != -1)
+  {
+    close(abortPipe[0]);
+    close(abortPipe[1]);
+    abortPipe[0] = -1;
+    abortPipe[1] = -1;
+  }
+}
+
+bool TCP::init()
+{
+  logger = Log::getInstance();
+  
+  if (pipe2(abortPipe, O_NONBLOCK) == -1)
+  {
+    logger->log("TCP", Log::CRIT, "pipe2 error");
+    return false;
+  }
+  
+  return true;
+}
+
+bool TCP::connect(const std::string& ip, USHORT port)
+{
+  if (connected) return false;
+
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+  struct addrinfo* aip;
+  char portString[10];
+  std::snprintf(portString, 10, "%u", port);
+  int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
+
+  if ((gaiResult != 0) || !aip)
+  {
+    logger->log("TCP", Log::CRIT, "getaddrinfo error");
+    return false;
+  }
+
+  sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
+  if (sockfd == -1) { logger->log("TCP", Log::CRIT, "socket error"); return false; }
+
+  fcntl(sockfd, F_SETFL, O_NONBLOCK);
+
+  errno = 0;
+  // There should only be one aip result..
+  int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
+
+  if (connectResult == 0) // success
+  {
+    freeaddrinfo(aip);
+    connected = true;
+    return true;
+  }
+
+  if (errno != EINPROGRESS)
+  {
+    close(sockfd);
+    sockfd = -1;
+    logger->log("TCP", Log::CRIT, "connect error");
+    return false;
+  }
+
+  // Wait for connect to complete
+  fd_set writefds;
+  struct timeval tv;
+  FD_ZERO(&writefds);
+  FD_SET(abortPipe[0], &writefds);
+  FD_SET(sockfd, &writefds);
+
+  tv.tv_sec = 5; // Allow 5s for a connect
+  tv.tv_usec = 0;
+
+  int selectResult = select((sockfd > abortPipe[0] ? sockfd : abortPipe[0]) + 1, NULL, &writefds, NULL, &tv);
+
+  if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
+  {
+    freeaddrinfo(aip);
+    logger->log("TCP", Log::INFO, "connected");
+    connected = true;
+    return true;
+  }
+  else
+  {
+    close(sockfd);
+    sockfd = -1;
+    logger->log("TCP", Log::CRIT, "connect/select error");
+    return false;
+  }
+}
+
+void TCP::shutdown()
+{
+  if (!connected) return;
+  connected = false;
+  close(sockfd);
+  sockfd = -1;
+}
+
+bool TCP::status()
+{
+  return connected;
+}
+
+bool TCP::write(void* src, ULONG numBytes)
+{
+  if (!connected) return false;
+
+  std::lock_guard<std::mutex> lg(writeMutex);
+  int result = send(sockfd, src, numBytes, 0);  // FIXME does send return < numBytes? Might need loop
+  if (result < 0) return false;
+  if (static_cast<ULONG>(result) != numBytes) return false;
+
+  return true;
+}
+
+bool TCP::read(void* dst, ULONG numBytes)
+{
+  if (!connected) return false;
+
+  fd_set readfds;
+  struct timeval tv;
+
+  ULONG totalReceived = 0;
+  int abortCount = 0;
+  UCHAR* pointer = static_cast<UCHAR*>(dst);
+  
+  do
+  {
+    if (abortCount == 5)
+    {
+      logger->log("TCP", Log::ERR, "abortCount = 5");
+      return false;
+    }
+    
+    FD_ZERO(&readfds);
+    FD_SET(abortPipe[0], &readfds);
+    FD_SET(sockfd, &readfds);
+
+    tv.tv_sec = 2;
+    tv.tv_usec = 0;
+
+    int selectResult = select((sockfd > abortPipe[0] ? sockfd : abortPipe[0]) + 1, &readfds, NULL, NULL, &tv);
+
+    if (selectResult == -1) { shutdown(); return false; }
+    if (selectResult == 0) return false;
+    if (FD_ISSET(abortPipe[0], &readfds)) return false;
+
+    int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
+    if (recvResult == -1) { shutdown(); return false; }
+    totalReceived += recvResult;
+    pointer += recvResult;
+    
+  } while (totalReceived < numBytes);
+  
+  return true;
+}
+
+MACAddress TCP::getMAC()
+{
+  MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
+
+  if (!connected) return macerror;
+
+  /*
+  struct sockaddr      - man 2 bind
+  struct sockaddr_in   - man 7 ip
+  struct sockaddr_in6  - man 7 ipv6
+  struct sockaddr_ll   - man 7 packet
+  */
+
+  // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
+  UINT buflen = 50;
+  unsigned char gsnBuffer[buflen];
+  struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
+  if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
+
+  uint32_t my4AddrU32;
+  unsigned char my6Addr[16];
+
+  if (sap->sa_family == AF_INET)
+  {
+    struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
+    my4AddrU32 = sap4->sin_addr.s_addr;
+  }
+  else if (sap->sa_family == AF_INET6)
+  {
+    struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
+    memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
+  }
+  else
+  {
+    return macerror;
+  }
+
+  struct ifaddrs* ifAddrs;
+  if (getifaddrs(&ifAddrs) == -1) return macerror;
+
+  char* ifName = NULL;
+
+  for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
+  {
+    if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
+
+    if (sap->sa_family == AF_INET)
+    {
+      struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
+      if (test->sin_addr.s_addr == my4AddrU32)
+      {
+        ifName = ifa->ifa_name;
+        break;
+      }
+    }
+    else if (sap->sa_family == AF_INET6)
+    {
+      struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
+      if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
+      {
+        ifName = ifa->ifa_name;
+        break;
+      }
+    }
+  }
+
+  if (!ifName)
+  {
+    freeifaddrs(ifAddrs);
+    return macerror;
+  }
+
+  // Walk the list again to get the MAC for ifName using family == AF_PACKET
+  MACAddress toReturn = macerror;
+
+  for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
+  {
+    if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
+    if (strcmp(ifName, ifa->ifa_name)) continue;
+    struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
+    if (sall->sll_halen != 6) continue;
+
+    memcpy(&toReturn, sall->sll_addr, 6);
+    break;
+  }
+
+  freeifaddrs(ifAddrs);
+  return toReturn;
+}
diff --git a/tcp.h b/tcp.h
new file mode 100644 (file)
index 0000000..8a26df5
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,56 @@
+/*
+    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 TCP_H
+#define TCP_H
+
+#include <mutex>
+
+#include "defines.h"
+
+struct MACAddress
+{
+  UCHAR mac[6];
+};
+
+class Log;
+
+class TCP
+{
+  public:
+    ~TCP();
+    bool init(); // Must call this first, once on any new TCP object
+    void shutdown(); // Optional. Closes connection. Can call connect() next
+    
+    bool connect(const std::string& ip, USHORT port);
+    bool read(void* dest, ULONG numBytes);
+    bool write(void* src, ULONG numBytes);
+    bool status();
+
+    MACAddress getMAC();
+
+  private:
+    Log* logger;
+    int sockfd{-1};
+    int abortPipe[2]{-1, -1};
+    bool connected{};
+    std::mutex writeMutex;
+};
+
+#endif
diff --git a/vdr.cc b/vdr.cc
index d0914824fef876be6994cd14663de5bf8de3e926..fe9969b4b792dccb0e4f1f219230944d7a38f415 100644 (file)
--- a/vdr.cc
+++ b/vdr.cc
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2019 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -20,8 +20,6 @@
 #include "vdr.h"
 
 #include "recman.h"
-#include "tcpold.h"
-#include "log.h"
 #include "recinfo.h"
 #include "channel.h"
 #include "event.h"
@@ -140,6 +138,7 @@ int VDR::init()
   if (initted) return 0;
   initted = 1;
   logger = Log::getInstance();
+  tcp.init();
   return 1;
 }
 
@@ -166,9 +165,8 @@ int VDR::connect()
   maxChannelNumber = 0;
   channelNumberWidth = 1;
 
-  if (tcpold) delete tcpold;
-  tcpold = new TCPOld();
-  if (tcpold->connectTo(serverIP, serverPort))
+  tcp.shutdown();
+  if (tcp.connect(serverIP, serverPort))
   {
     connected = true;
     threadStart();
@@ -183,15 +181,13 @@ int VDR::connect()
 void VDR::disconnect()
 {
   threadCancel();
-  if (tcpold) delete tcpold;
-  tcpold = NULL;
   connected = false;
   logger->log("VDR", Log::DEBUG, "Disconnect");
 }
 
 void VDR::setReceiveWindow(size_t size)
 {
-  if (connected && size) tcpold->setReceiveWindow(size);
+  //if (connected && size) tcpold->setReceiveWindow(size);
 }
 
 ///////////////////////////////////////////////////////
@@ -216,18 +212,18 @@ void VDR::threadMethod()
   ULONG timeNow = 0;
   ULONG lastKAsent = 0;
   ULONG lastKArecv = time(NULL);
-  int readSuccess;
+  bool readSuccess;
 
   while(1)
   {
     timeNow = time(NULL);
     
-    readSuccess = tcpold->readData(&channelID, sizeof(ULONG));  // 2s timeout atm
+    readSuccess = tcp.read(&channelID, sizeof(ULONG));  // 2s timeout atm
 
     if (!readSuccess)
     {
       //logger->log("VDR", Log::DEBUG, "Net read timeout");
-      if (!tcpold->isConnected()) { connectionDied(); return; } // return to stop this thread
+      if (!tcp.status()) { connectionDied(); return; } // return to stop this thread
     }
       
     // Error or timeout.
@@ -264,9 +260,9 @@ void VDR::threadMethod()
 
     if (channelID == CHANNEL_REQUEST_RESPONSE)
     {
-      if (!tcpold->readData(&requestID, sizeof(ULONG))) break;
+      if (!tcp.read(&requestID, sizeof(ULONG))) break;
       requestID = ntohl(requestID);
-      if (!tcpold->readData(&userDataLength, sizeof(ULONG))) break;
+      if (!tcp.read(&userDataLength, sizeof(ULONG))) break;
       userDataLength = ntohl(userDataLength);
       if (userDataLength > 5000000) break; // how big can these packets get?
       userData = NULL;
@@ -274,7 +270,7 @@ void VDR::threadMethod()
       {
         userData = malloc(userDataLength);
         if (!userData) break;
-        if (!tcpold->readData(userData, userDataLength)) break;
+        if (!tcp.read(userData, userDataLength)) break;
       }
 
       vresp = new VDR_ResponsePacket();  
@@ -290,20 +286,20 @@ void VDR::threadMethod()
     }
     else if (channelID == CHANNEL_STREAM || channelID == CHANNEL_TVMEDIA)
     {
-      if (!tcpold->readData(&streamID, sizeof(ULONG))) break;
+      if (!tcp.read(&streamID, sizeof(ULONG))) break;
       streamID = ntohl(streamID);
 
-      if (!tcpold->readData(&flag, sizeof(ULONG))) break;
+      if (!tcp.read(&flag, sizeof(ULONG))) break;
       flag = ntohl(flag);
 
-      if (!tcpold->readData(&userDataLength, sizeof(ULONG))) break;
+      if (!tcp.read(&userDataLength, sizeof(ULONG))) break;
       userDataLength = ntohl(userDataLength);
       userData = NULL;
       if (userDataLength > 0)
       {
         userData = malloc(userDataLength);
         if (!userData) break;
-        if (!tcpold->readData(userData, userDataLength)) break;
+        if (!tcp.read(userData, userDataLength)) break;
       }
 
       vresp = new VDR_ResponsePacket();    
@@ -320,7 +316,7 @@ void VDR::threadMethod()
     else if (channelID == CHANNEL_KEEPALIVE)
     {
       ULONG KAreply = 0;
-      if (!tcpold->readData(&KAreply, sizeof(ULONG))) break;
+      if (!tcp.read(&KAreply, sizeof(ULONG))) break;
       KAreply = ntohl(KAreply);
       if (KAreply == lastKAsent) // successful KA response
       {
@@ -460,7 +456,7 @@ VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
   edMutex.lock();
 
 
-  if (static_cast<ULONG>(tcpold->sendData(vrp->getPtr(), vrp->getLen())) != vrp->getLen())
+  if (!tcp.write(vrp->getPtr(), vrp->getLen()))
   {
     edMutex.unlock();
     edUnregister(&vdrpr);
@@ -495,8 +491,7 @@ bool VDR::sendKA(ULONG timeStamp)
   buffer[pos++]=(ul>>16)&0xff;
   buffer[pos++]=(ul>>8)&0xff;
   buffer[pos++]=ul &0xff;
-  if (static_cast<ULONG>(tcpold->sendData(buffer, 8)) != 8) return false;
-  return true;
+  return tcp.write(buffer, 8);
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -557,9 +552,8 @@ int VDR::doLogin(unsigned int* v_server_min, unsigned int* v_server_max, unsigne
   VDR_RequestPacket vrp;
   if (!vrp.init(VDR_LOGIN, true, 6)) return 0;
 
-  UCHAR mactemp[6];
-  tcpold->getMAC(mactemp);
-  if (!vrp.copyin(mactemp, 6)) return 0;
+  MACAddress myMAC = tcp.getMAC();
+  if (!vrp.copyin(reinterpret_cast<UCHAR*>(&myMAC), 6)) return 0;
 
   VDR_ResponsePacket* vresp = RequestResponse(&vrp);
   if (vresp->noResponse()) { delete vresp; return 0; }
@@ -661,7 +655,7 @@ bool VDR::LogExtern(const char* logString)
 
   strcpy(&buffer[8], logString);
   
-  if (tcpold->sendData(buffer, packetLength) != packetLength)
+  if (!tcp.write(buffer, packetLength))
   {
     connected = false; // stop the rest of the connection  
     delete [] buffer;
diff --git a/vdr.h b/vdr.h
index af2e254663d2a33cda187f9a1d5cea974ff27499..c00ea11fbf36bab641804bf70b2bc9d85d93262f 100644 (file)
--- a/vdr.h
+++ b/vdr.h
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2019 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -42,9 +42,8 @@
 #include "i18n.h"
 #include "log.h"
 #include "command.h"
+#include "tcp.h"
 
-class TCPOld;
-class Log;
 class RecInfo;
 class Event;
 class Channel;
@@ -233,7 +232,7 @@ public ExternLogger
     Log* logger;
     int initted{};
     int findingServer{};
-    TCPOld* tcpold{};
+    TCP tcp;
     char serverIP[40];
     USHORT serverPort;
     bool connected{};