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