--- /dev/null
+/*
+ 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;
+}
/*
- Copyright 2004-2019 Chris Tallon
+ Copyright 2004-2020 Chris Tallon
This file is part of VOMP.
#include "vdr.h"
#include "recman.h"
-#include "tcpold.h"
-#include "log.h"
#include "recinfo.h"
#include "channel.h"
#include "event.h"
if (initted) return 0;
initted = 1;
logger = Log::getInstance();
+ tcp.init();
return 1;
}
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();
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);
}
///////////////////////////////////////////////////////
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.
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;
{
userData = malloc(userDataLength);
if (!userData) break;
- if (!tcpold->readData(userData, userDataLength)) break;
+ if (!tcp.read(userData, userDataLength)) break;
}
vresp = new VDR_ResponsePacket();
}
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();
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
{
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);
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);
}
/////////////////////////////////////////////////////////////////////////////
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; }
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;