]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
Fix VRecording showing graphic at bottom left when it shouldn't
[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   #include <sys/un.h>  // uds
35 #endif
36
37 #include <sys/types.h>  // socket connect getaddrinfo
38 #include <string.h> // memset
39 #include <errno.h> // errno var
40 #include <stdlib.h> // malloc / free
41
42 #include <sstream>
43
44 #include "defines.h"
45 #include "log.h"
46
47 #include "tcp.h"
48
49 static const char* TAG = "TCP";
50
51 TCP::~TCP()
52 {
53   shutdown();
54   
55 #ifdef WIN32
56   CLOSESOCKET(abortSocket);
57 #else
58   if (abortPipe[0] != -1)
59   {
60     close(abortPipe[0]);
61     close(abortPipe[1]);
62     abortPipe[0] = -1;
63     abortPipe[1] = -1;
64   }
65 #endif
66 }
67
68 bool TCP::init()
69 {
70   logger = LogNT::getInstance();
71   
72   #ifdef WIN32
73   abortSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
74   if (abortSocket == INVALID_SOCKET)
75   {
76     logger->crit(TAG, "socket error");
77     return false;
78   }
79   #else
80   if (pipe2(abortPipe, O_NONBLOCK) == -1)
81   {
82     logger->crit(TAG, "pipe2 error");
83     return false;
84   }
85   #endif
86   
87   return true;
88 }
89
90 #ifndef WIN32
91 bool TCP::connectSocket(const std::string& socketFile)
92 {
93   if (connected) return false;
94
95   int pathLength = strlen(socketFile.c_str()); // Specifically use strlen rather than std::string length stuff
96   if (pathLength > 107) { logger->crit(TAG, "socket name too long"); return false; }
97
98   sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
99   if (sockfd == -1) { logger->crit(TAG, "socket error"); return false; }
100
101   fcntl(sockfd, F_SETFL, O_NONBLOCK);
102
103   struct sockaddr_un uds;
104   uds.sun_family = AF_UNIX;
105   strcpy(uds.sun_path, socketFile.c_str());
106   int connectResult = ::connect(sockfd, reinterpret_cast<struct sockaddr *>(&uds), pathLength + sizeof(uds.sun_family));
107
108   if (connectResult == 0) // success
109   {
110     connected = true;
111     return true;
112   }
113
114   return waitForConnect(connectResult);
115 }
116 #endif
117
118 bool TCP::connect(const std::string& ip, USHORT port)
119 {
120   if (connected) return false;
121
122   struct addrinfo hints;
123   memset(&hints, 0, sizeof(struct addrinfo));
124   hints.ai_family = AF_UNSPEC;
125   hints.ai_socktype = SOCK_STREAM;
126   hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
127
128   struct addrinfo* aip;
129   char portString[10];
130   SNPRINTF(portString, 10, "%u", port);
131   int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
132
133   if ((gaiResult != 0) || !aip)
134   {
135     logger->crit(TAG, "getaddrinfo error");
136     return false;
137   }
138
139   sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
140   if (sockfd == -1) { logger->crit(TAG, "socket error"); return false; }
141
142 #ifdef WIN32
143   ULONG param = 1;
144   ioctlsocket(sockfd, FIONBIO, &param);
145 #else
146   fcntl(sockfd, F_SETFL, O_NONBLOCK);
147 #endif
148
149   errno = 0;
150   // There should only be one aip result..
151   int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
152
153   freeaddrinfo(aip);
154
155   if (connectResult == 0) // success
156   {
157     connected = true;
158     return true;
159   }
160
161   return waitForConnect(connectResult);
162 }
163
164 #ifdef WIN32
165 bool TCP::waitForConnect(int connectResult)
166 {
167   if ((connectResult != SOCKET_ERROR) || (WSAGetLastError() != WSAEWOULDBLOCK))
168 #else
169 bool TCP::waitForConnect(int)
170 {
171   if (errno != EINPROGRESS)
172 #endif
173   {
174     CLOSESOCKET(sockfd);
175     sockfd = -1;
176     logger->crit(TAG, "connect error");
177     return false;
178   }
179
180   // Wait for connect to complete
181   fd_set writefds;
182   FD_ZERO(&writefds);
183   FD_SET(sockfd, &writefds);
184   int maxfd = sockfd;
185
186   fd_set readfds;
187   FD_ZERO(&readfds);
188   FD_SET(sockfd, &readfds);
189
190
191 #ifdef WIN32
192   FD_SET(abortSocket, &readfds);
193 #else
194   FD_SET(abortPipe[0], &readfds);
195   if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
196 #endif
197
198   struct timeval tv;
199   tv.tv_sec = 10; // Allow 10s for a connect
200   tv.tv_usec = 0;
201
202   int selectResult = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
203 #ifdef WIN32
204   if (FD_ISSET(abortSocket, &readfds))
205 #else
206   if (FD_ISSET(abortPipe[0], &readfds))
207 #endif
208   {
209     CLOSESOCKET(sockfd);
210     sockfd = -1;
211     logger->info(TAG, "connect/select aborting");
212     return false;
213   }
214
215   logger->trace(TAG, "waitForConnect(): selectResult = {}", selectResult);
216
217   if ((selectResult == 1) && FD_ISSET(sockfd, &writefds))
218   {
219     logger->info(TAG, "Connected");
220     connected = true;
221     return true;
222   }
223   else if ((selectResult == 2) && FD_ISSET(sockfd, &readfds) && FD_ISSET(sockfd, &writefds))
224   {
225     CLOSESOCKET(sockfd);
226     sockfd = -1;
227     logger->crit(TAG, "Detected connection failed");
228     return false;
229   }
230   else
231   {
232     CLOSESOCKET(sockfd);
233     sockfd = -1;
234     logger->crit(TAG, "connect/select error");
235     return false;
236   }
237 }
238
239 void TCP::shutdown()
240 {
241   if (!connected) return;
242   connected = false;
243   CLOSESOCKET(sockfd);
244   sockfd = -1;
245
246   char waste[10];
247 #ifdef WIN32
248   while (recv(abortSocket, waste, 10, 0) > 0) ;
249 #else
250   while (::read(abortPipe[0], waste, 10) > 0) ;
251 #endif
252
253   if (recStringBuf)
254   {
255     free(recStringBuf);
256     recStringBuf = NULL;
257     recStringBufStart = 0;
258     recStringBufUsed = 0;
259   }
260 }
261
262 bool TCP::status()
263 {
264   return connected;
265 }
266
267 void TCP::abortCall()
268 {
269 #ifdef WIN32
270   CLOSESOCKET(abortSocket);
271 #else
272   ::write(abortPipe[1], "X", 1);
273 #endif
274 }
275
276 bool TCP::write(void* src, ULONG numBytes)
277 {
278   if (!connected) return false;
279
280   std::lock_guard<std::mutex> lg(writeMutex);
281   int result = send(sockfd, reinterpret_cast<char*>(src), numBytes, 0);  // FIXME does send return < numBytes? Might need loop
282   if (result < 0) return false;
283   if (static_cast<ULONG>(result) != numBytes) return false;
284
285   return true;
286 }
287
288 bool TCP::read(void* dst, ULONG numBytes, int timeoutSec)
289 {
290   if (!connected) return false;
291
292   fd_set readfds;
293   struct timeval tv;
294
295   ULONG totalReceived = 0;
296   int abortCount = 0;
297   char* pointer = static_cast<char*>(dst); // WIN32 requires char*
298   
299   do
300   {
301     if (++abortCount == 1000)
302     {
303       logger->error(TAG, "abortCount = 1000 - runaway error, or packet arrived in > 1000 pieces??");
304       return false;
305     }
306     
307     FD_ZERO(&readfds);
308     FD_SET(sockfd, &readfds);
309     int maxfd = sockfd; // WIN32 ignores
310
311 #ifdef WIN32
312     FD_SET(abortSocket, &readfds);
313 #else
314     FD_SET(abortPipe[0], &readfds);
315     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
316 #endif
317
318     int selectResult;
319     if (timeoutSec)
320     {
321       tv.tv_sec = timeoutSec;
322       tv.tv_usec = 0;
323       selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
324     }
325     else
326     {
327       selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
328     }
329
330     if (selectResult == -1) { shutdown(); return false; }
331     if (selectResult == 0) return false;
332
333 #ifdef WIN32
334     if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
335 #else
336     if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
337 #endif
338   
339     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
340     if (recvResult == -1) { shutdown(); return false; }
341     totalReceived += recvResult;
342     pointer += recvResult;
343
344   } while (totalReceived < numBytes);
345   
346   return true;
347 }
348
349 std::stringstream TCP::readString(bool* result, int timeoutSec)
350 {
351   *result = false;
352
353   std::stringstream ss;
354
355   if (!connected) return ss;
356
357   fd_set readfds;
358   struct timeval tv;
359
360   int abortCount = 0;
361
362   if (!recStringBuf) recStringBuf = static_cast<char*>(malloc(recStringBufSize));
363   if (!recStringBuf) return ss;
364
365   // Absorb over-read from last time?
366   if (recStringBufUsed)
367   {
368     ss.write(&recStringBuf[recStringBufStart], recStringBufUsed);
369     recStringBufStart = 0;
370     recStringBufUsed = 0;
371   }
372
373   while(true)
374   {
375     if (++abortCount == 20)
376     {
377       logger->debug(TAG, "abortCount = 20");
378       return ss;
379     }
380
381     FD_ZERO(&readfds);
382     FD_SET(sockfd, &readfds);
383     int maxfd = sockfd; // WIN32 ignores
384
385 #ifdef WIN32
386     FD_SET(abortSocket, &readfds);
387 #else
388     FD_SET(abortPipe[0], &readfds);
389     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
390 #endif
391
392     int selectResult;
393     if (timeoutSec)
394     {
395       tv.tv_sec = timeoutSec;
396       tv.tv_usec = 0;
397       selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
398     }
399     else
400     {
401       selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
402     }
403
404     if (selectResult == -1) { shutdown(); return ss; }
405     if (selectResult == 0) return ss;
406
407 #ifdef WIN32
408     if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
409 #else
410     if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
411 #endif
412
413     int recvResult = recv(sockfd, &recStringBuf[recStringBufUsed], recStringBufSize - recStringBufUsed, 0);
414     if (recvResult == -1) { shutdown(); return ss; }
415     recStringBufUsed += recvResult;
416
417     // Do we have a full string?
418     for(int i = 0; i < recStringBufUsed; i++)
419     {
420       if (recStringBuf[i] == '\n')
421       {
422         // Found.
423         ss.write(recStringBuf, i);
424
425         if ((i + 1) != recStringBufUsed) // over read
426         {
427           i += 1; // Advance over \n
428           recStringBufStart = i;
429           recStringBufUsed -= i;
430         }
431         else
432         {
433           recStringBufUsed = 0;
434         }
435
436         *result = true;
437         return ss;
438       }
439     }
440     // no \n in buffer, go around
441   }
442 }
443
444 MACAddress TCP::getMAC()
445 {
446 #ifndef WIN32
447   MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
448   if (!connected) return macerror;
449
450   /*
451   struct sockaddr      - man 2 bind
452   struct sockaddr_in   - man 7 ip
453   struct sockaddr_in6  - man 7 ipv6
454   struct sockaddr_ll   - man 7 packet
455   */
456
457   // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
458   UINT buflen = 50;
459   unsigned char gsnBuffer[buflen];
460   struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
461   if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
462
463   uint32_t my4AddrU32;
464   unsigned char my6Addr[16];
465
466   if (sap->sa_family == AF_INET)
467   {
468     struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
469     my4AddrU32 = sap4->sin_addr.s_addr;
470   }
471   else if (sap->sa_family == AF_INET6)
472   {
473     struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
474     memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
475   }
476   else
477   {
478     return macerror;
479   }
480
481   struct ifaddrs* ifAddrs;
482   if (getifaddrs(&ifAddrs) == -1) return macerror;
483
484   char* ifName = NULL;
485
486   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
487   {
488     if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
489
490     if (sap->sa_family == AF_INET)
491     {
492       struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
493       if (test->sin_addr.s_addr == my4AddrU32)
494       {
495         ifName = ifa->ifa_name;
496         break;
497       }
498     }
499     else if (sap->sa_family == AF_INET6)
500     {
501       struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
502       if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
503       {
504         ifName = ifa->ifa_name;
505         break;
506       }
507     }
508   }
509
510   if (!ifName)
511   {
512     freeifaddrs(ifAddrs);
513     return macerror;
514   }
515
516   // Walk the list again to get the MAC for ifName using family == AF_PACKET
517   MACAddress toReturn = macerror;
518
519   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
520   {
521     if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
522     if (strcmp(ifName, ifa->ifa_name)) continue;
523     struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
524     if (sall->sll_halen != 6) continue;
525
526     memcpy(&toReturn, sall->sll_addr, 6);
527     break;
528   }
529
530   freeifaddrs(ifAddrs);
531   return toReturn;
532
533 #else // WIN32
534   PIP_ADAPTER_INFO daptinfo = NULL;
535   DWORD size = 0;
536   GetAdaptersInfo(daptinfo, &size);
537   daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
538   MACAddress macresult;
539   memcpy(&macresult, "ABCDEF", 6);//Dummy Address
540   sockaddr_in sock_address;
541   int sockname_len = sizeof(sock_address);
542   getsockname(sockfd, (sockaddr*)&sock_address, &sockname_len);
543   ULONG sockip = sock_address.sin_addr.s_addr;
544   if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
545   {
546     PIP_ADAPTER_INFO daptinfo_it = daptinfo;
547     while (daptinfo_it != NULL)
548     {
549       ULONG ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
550       if (ipaddress == sockip)
551       { //Is it our MAC?
552         memcpy(&macresult, daptinfo_it->Address, 6);
553         break;
554       }
555       daptinfo_it = daptinfo_it->Next;
556       if (daptinfo_it == daptinfo) break;
557     }
558   }
559   delete[] daptinfo;
560   return macresult;
561 #endif
562 }