]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
VDR connection bug fixes
[vompclient.git] / tcp.cc
1 /*
2     Copyright 2020 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <string.h> // memcmp
21
22 #ifdef WIN32
23   #include <WS2tcpip.h>
24   #include <iphlpapi.h>
25 #else
26   #include <unistd.h> // pipe2
27   #include <fcntl.h> // pipe2-O_NONBLOCK fcntl
28   #include <sys/socket.h> // socket connect getaddrinfo
29   #include <netdb.h> // getaddrinfo
30   #include <sys/select.h> // select
31   #include <linux/if_packet.h> // for getMAC
32   #include <net/ethernet.h> // for getMAC
33   #include <ifaddrs.h> // getifaddrs
34 #endif
35
36 #include <sys/types.h>  // socket connect getaddrinfo
37 #include <string.h> // memset
38 #include <errno.h> // errno var
39
40 #include "defines.h"
41 #include "log.h"
42
43 #include "tcp.h"
44
45 TCP::~TCP()
46 {
47   shutdown();
48   
49 #ifdef WIN32
50   CLOSESOCKET(abortSocket);
51 #else
52   if (abortPipe[0] != -1)
53   {
54     close(abortPipe[0]);
55     close(abortPipe[1]);
56     abortPipe[0] = -1;
57     abortPipe[1] = -1;
58   }
59 #endif
60 }
61
62 bool TCP::init()
63 {
64   logger = Log::getInstance();
65   
66   #ifdef WIN32
67   abortSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
68   if (abortSocket == INVALID_SOCKET)
69   {
70     logger->log("TCP", Log::CRIT, "socket error");
71     return false;
72   }
73   #else
74   if (pipe2(abortPipe, O_NONBLOCK) == -1)
75   {
76     logger->log("TCP", Log::CRIT, "pipe2 error");
77     return false;
78   }
79   #endif
80   
81   return true;
82 }
83
84 bool TCP::connect(const std::string& ip, USHORT port)
85 {
86   if (connected) return false;
87
88   struct addrinfo hints;
89   memset(&hints, 0, sizeof(struct addrinfo));
90   hints.ai_family = AF_UNSPEC;
91   hints.ai_socktype = SOCK_STREAM;
92   hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
93
94   struct addrinfo* aip;
95   char portString[10];
96   SNPRINTF(portString, 10, "%u", port);
97   int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
98
99   if ((gaiResult != 0) || !aip)
100   {
101     logger->log("TCP", Log::CRIT, "getaddrinfo error");
102     return false;
103   }
104
105   sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
106   if (sockfd == -1) { logger->log("TCP", Log::CRIT, "socket error"); return false; }
107
108 #ifdef WIN32
109   ULONG param = 1;
110   ioctlsocket(sockfd, FIONBIO, &param);
111 #else
112   fcntl(sockfd, F_SETFL, O_NONBLOCK);
113 #endif
114
115   errno = 0;
116   // There should only be one aip result..
117   int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
118
119   if (connectResult == 0) // success
120   {
121     freeaddrinfo(aip);
122     connected = true;
123     return true;
124   }
125
126 #ifdef WIN32
127   if ((connectResult != SOCKET_ERROR) || (WSAGetLastError() != WSAEWOULDBLOCK))
128 #else
129   if (errno != EINPROGRESS)
130 #endif
131   {
132     CLOSESOCKET(sockfd);
133     sockfd = -1;
134     logger->log("TCP", Log::CRIT, "connect error");
135     return false;
136   }
137
138   // Wait for connect to complete
139   fd_set writefds;
140   FD_ZERO(&writefds);
141   FD_SET(sockfd, &writefds);
142   int maxfd = sockfd;
143
144   fd_set readfds;
145   FD_ZERO(&readfds);
146
147
148 #ifdef WIN32
149   FD_SET(abortSocket, &readfds);
150 #else
151   FD_SET(abortPipe[0], &readfds);
152   if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
153 #endif
154
155   struct timeval tv;
156   tv.tv_sec = 20; // Allow 5s for a connect
157   tv.tv_usec = 0;
158
159   int selectResult = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
160 #ifdef WIN32
161   if (FD_ISSET(abortSocket, &readfds))
162 #else
163   if (FD_ISSET(abortPipe[0], &readfds))
164 #endif
165   {
166     CLOSESOCKET(sockfd);
167     sockfd = -1;
168     logger->log("TCP", Log::INFO, "connect/select aborting");
169     return false;
170   }
171
172   if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
173   {
174     freeaddrinfo(aip);
175     logger->log("TCP", Log::INFO, "Connected");
176     connected = true;
177     return true;
178   }
179   else
180   {
181     CLOSESOCKET(sockfd);
182     sockfd = -1;
183     logger->log("TCP", Log::CRIT, "connect/select error");
184     return false;
185   }
186 }
187
188 void TCP::shutdown()
189 {
190   if (!connected) return;
191   connected = false;
192   CLOSESOCKET(sockfd);
193   sockfd = -1;
194
195   char waste[10];
196 #ifdef WIN32
197   while (recv(abortSocket, waste, 10, 0) > 0) ;
198 #else
199   while (::read(abortPipe[0], waste, 10) > 0) ;
200 #endif
201 }
202
203 bool TCP::status()
204 {
205   return connected;
206 }
207
208 void TCP::abortCall()
209 {
210 #ifdef WIN32
211   CLOSESOCKET(abortSocket);
212 #else
213   ::write(abortPipe[1], "X", 1);
214 #endif
215 }
216
217 bool TCP::write(void* src, ULONG numBytes)
218 {
219   if (!connected) return false;
220
221   std::lock_guard<std::mutex> lg(writeMutex);
222   int result = send(sockfd, reinterpret_cast<char*>(src), numBytes, 0);  // FIXME does send return < numBytes? Might need loop
223   if (result < 0) return false;
224   if (static_cast<ULONG>(result) != numBytes) return false;
225
226   return true;
227 }
228
229 bool TCP::read(void* dst, ULONG numBytes)
230 {
231   if (!connected) return false;
232
233   fd_set readfds;
234   struct timeval tv;
235
236   ULONG totalReceived = 0;
237   int abortCount = 0;
238   char* pointer = static_cast<char*>(dst); // WIN32 requires char*
239   
240   do
241   {
242     if (abortCount == 5)
243     {
244       logger->log("TCP", Log::DEBUG, "abortCount = 5");
245       return false;
246     }
247     
248     FD_ZERO(&readfds);
249     FD_SET(sockfd, &readfds);
250     int maxfd = sockfd; // WIN32 ignores
251
252 #ifdef WIN32
253     FD_SET(abortSocket, &readfds);
254 #else
255     FD_SET(abortPipe[0], &readfds);
256     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
257 #endif
258
259     tv.tv_sec = 2;
260     tv.tv_usec = 0;
261
262     int selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
263
264     if (selectResult == -1) { shutdown(); return false; }
265     if (selectResult == 0) return false;
266
267 #ifdef WIN32
268   if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
269 #else
270
271
272
273   if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "B aborting..."); return false; }
274 #endif
275   
276     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
277     if (recvResult == -1) { shutdown(); return false; }
278     totalReceived += recvResult;
279     pointer += recvResult;
280     
281   } while (totalReceived < numBytes);
282   
283   return true;
284 }
285
286 MACAddress TCP::getMAC()
287 {
288 #ifndef WIN32
289   MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
290   if (!connected) return macerror;
291
292   /*
293   struct sockaddr      - man 2 bind
294   struct sockaddr_in   - man 7 ip
295   struct sockaddr_in6  - man 7 ipv6
296   struct sockaddr_ll   - man 7 packet
297   */
298
299   // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
300   UINT buflen = 50;
301   unsigned char gsnBuffer[buflen];
302   struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
303   if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
304
305   uint32_t my4AddrU32;
306   unsigned char my6Addr[16];
307
308   if (sap->sa_family == AF_INET)
309   {
310     struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
311     my4AddrU32 = sap4->sin_addr.s_addr;
312   }
313   else if (sap->sa_family == AF_INET6)
314   {
315     struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
316     memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
317   }
318   else
319   {
320     return macerror;
321   }
322
323   struct ifaddrs* ifAddrs;
324   if (getifaddrs(&ifAddrs) == -1) return macerror;
325
326   char* ifName = NULL;
327
328   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
329   {
330     if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
331
332     if (sap->sa_family == AF_INET)
333     {
334       struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
335       if (test->sin_addr.s_addr == my4AddrU32)
336       {
337         ifName = ifa->ifa_name;
338         break;
339       }
340     }
341     else if (sap->sa_family == AF_INET6)
342     {
343       struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
344       if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
345       {
346         ifName = ifa->ifa_name;
347         break;
348       }
349     }
350   }
351
352   if (!ifName)
353   {
354     freeifaddrs(ifAddrs);
355     return macerror;
356   }
357
358   // Walk the list again to get the MAC for ifName using family == AF_PACKET
359   MACAddress toReturn = macerror;
360
361   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
362   {
363     if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
364     if (strcmp(ifName, ifa->ifa_name)) continue;
365     struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
366     if (sall->sll_halen != 6) continue;
367
368     memcpy(&toReturn, sall->sll_addr, 6);
369     break;
370   }
371
372   freeifaddrs(ifAddrs);
373   return toReturn;
374
375 #else // WIN32
376   PIP_ADAPTER_INFO daptinfo = NULL;
377   DWORD size = 0;
378   GetAdaptersInfo(daptinfo, &size);
379   daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
380   MACAddress macresult;
381   memcpy(&macresult, "ABCDEF", 6);//Dummy Address
382   sockaddr_in sock_address;
383   int sockname_len = sizeof(sock_address);
384   getsockname(sockfd, (sockaddr*)&sock_address, &sockname_len);
385   ULONG sockip = sock_address.sin_addr.s_addr;
386   if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
387   {
388     PIP_ADAPTER_INFO daptinfo_it = daptinfo;
389     while (daptinfo_it != NULL)
390     {
391       ULONG ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
392       if (ipaddress == sockip)
393       { //Is it our MAC?
394         memcpy(&macresult, daptinfo_it->Address, 6);
395         break;
396       }
397       daptinfo_it = daptinfo_it->Next;
398       if (daptinfo_it == daptinfo) break;
399     }
400   }
401   delete[] daptinfo;
402   return macresult;
403 #endif
404 }