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