]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
Remove deprecated max() function
[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   if ((selectResult == 1) && FD_ISSET(sockfd, &writefds))
216   {
217     logger->info(TAG, "Connected");
218     connected = true;
219     return true;
220   }
221   else if ((selectResult == 2) && FD_ISSET(sockfd, &readfds) && FD_ISSET(sockfd, &writefds))
222   {
223     CLOSESOCKET(sockfd);
224     sockfd = -1;
225     logger->crit(TAG, "Detected connection failed");
226     return false;
227   }
228   else
229   {
230     CLOSESOCKET(sockfd);
231     sockfd = -1;
232     logger->crit(TAG, "connect/select error");
233     return false;
234   }
235 }
236
237 void TCP::shutdown()
238 {
239   if (!connected) return;
240   connected = false;
241   CLOSESOCKET(sockfd);
242   sockfd = -1;
243
244   char waste[10];
245 #ifdef WIN32
246   while (recv(abortSocket, waste, 10, 0) > 0) ;
247 #else
248   while (::read(abortPipe[0], waste, 10) > 0) ;
249 #endif
250
251   if (recStringBuf)
252   {
253     free(recStringBuf);
254     recStringBuf = NULL;
255     recStringBufStart = 0;
256     recStringBufUsed = 0;
257   }
258 }
259
260 bool TCP::status()
261 {
262   return connected;
263 }
264
265 void TCP::abortCall()
266 {
267 #ifdef WIN32
268   CLOSESOCKET(abortSocket);
269 #else
270   ::write(abortPipe[1], "X", 1);
271 #endif
272 }
273
274 bool TCP::write(void* src, ULONG numBytes)
275 {
276   if (!connected) return false;
277
278   std::lock_guard<std::mutex> lg(writeMutex);
279   int result = send(sockfd, reinterpret_cast<char*>(src), numBytes, 0);  // FIXME does send return < numBytes? Might need loop
280   if (result < 0) return false;
281   if (static_cast<ULONG>(result) != numBytes) return false;
282
283   return true;
284 }
285
286 bool TCP::read(void* dst, ULONG numBytes, int timeoutSec)
287 {
288   if (!connected) return false;
289
290   fd_set readfds;
291   struct timeval tv;
292
293   ULONG totalReceived = 0;
294   int abortCount = 0;
295   char* pointer = static_cast<char*>(dst); // WIN32 requires char*
296   
297   do
298   {
299     if (++abortCount == 1000)
300     {
301       logger->error(TAG, "abortCount = 1000 - runaway error, or packet arrived in > 1000 pieces??");
302       return false;
303     }
304     
305     FD_ZERO(&readfds);
306     FD_SET(sockfd, &readfds);
307     int maxfd = sockfd; // WIN32 ignores
308
309 #ifdef WIN32
310     FD_SET(abortSocket, &readfds);
311 #else
312     FD_SET(abortPipe[0], &readfds);
313     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
314 #endif
315
316     int selectResult;
317     if (timeoutSec)
318     {
319       tv.tv_sec = timeoutSec;
320       tv.tv_usec = 0;
321       selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
322     }
323     else
324     {
325       selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
326     }
327
328     if (selectResult == -1) { shutdown(); return false; }
329     if (selectResult == 0) return false;
330
331 #ifdef WIN32
332     if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
333 #else
334     if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
335 #endif
336   
337     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
338     if (recvResult == -1) { shutdown(); return false; }
339     totalReceived += recvResult;
340     pointer += recvResult;
341
342   } while (totalReceived < numBytes);
343   
344   return true;
345 }
346
347 std::stringstream TCP::readString(bool* result, int timeoutSec)
348 {
349   *result = false;
350
351   std::stringstream ss;
352
353   if (!connected) return ss;
354
355   fd_set readfds;
356   struct timeval tv;
357
358   int abortCount = 0;
359
360   if (!recStringBuf) recStringBuf = static_cast<char*>(malloc(recStringBufSize));
361   if (!recStringBuf) return ss;
362
363   // Absorb over-read from last time?
364   if (recStringBufUsed)
365   {
366     ss.write(&recStringBuf[recStringBufStart], recStringBufUsed);
367     recStringBufStart = 0;
368     recStringBufUsed = 0;
369   }
370
371   while(true)
372   {
373     if (++abortCount == 20)
374     {
375       logger->debug(TAG, "abortCount = 20");
376       return ss;
377     }
378
379     FD_ZERO(&readfds);
380     FD_SET(sockfd, &readfds);
381     int maxfd = sockfd; // WIN32 ignores
382
383 #ifdef WIN32
384     FD_SET(abortSocket, &readfds);
385 #else
386     FD_SET(abortPipe[0], &readfds);
387     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
388 #endif
389
390     int selectResult;
391     if (timeoutSec)
392     {
393       tv.tv_sec = timeoutSec;
394       tv.tv_usec = 0;
395       selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
396     }
397     else
398     {
399       selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
400     }
401
402     if (selectResult == -1) { shutdown(); return ss; }
403     if (selectResult == 0) return ss;
404
405 #ifdef WIN32
406     if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
407 #else
408     if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
409 #endif
410
411     int recvResult = recv(sockfd, &recStringBuf[recStringBufUsed], recStringBufSize - recStringBufUsed, 0);
412     if (recvResult == -1) { shutdown(); return ss; }
413     recStringBufUsed += recvResult;
414
415     // Do we have a full string?
416     for(int i = 0; i < recStringBufUsed; i++)
417     {
418       if (recStringBuf[i] == '\n')
419       {
420         // Found.
421         ss.write(recStringBuf, i);
422
423         if ((i + 1) != recStringBufUsed) // over read
424         {
425           i += 1; // Advance over \n
426           recStringBufStart = i;
427           recStringBufUsed -= i;
428         }
429         else
430         {
431           recStringBufUsed = 0;
432         }
433
434         *result = true;
435         return ss;
436       }
437     }
438     // no \n in buffer, go around
439   }
440 }
441
442 MACAddress TCP::getMAC()
443 {
444 #ifndef WIN32
445   MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
446   if (!connected) return macerror;
447
448   /*
449   struct sockaddr      - man 2 bind
450   struct sockaddr_in   - man 7 ip
451   struct sockaddr_in6  - man 7 ipv6
452   struct sockaddr_ll   - man 7 packet
453   */
454
455   // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
456   UINT buflen = 50;
457   unsigned char gsnBuffer[buflen];
458   struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
459   if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
460
461   uint32_t my4AddrU32;
462   unsigned char my6Addr[16];
463
464   if (sap->sa_family == AF_INET)
465   {
466     struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
467     my4AddrU32 = sap4->sin_addr.s_addr;
468   }
469   else if (sap->sa_family == AF_INET6)
470   {
471     struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
472     memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
473   }
474   else
475   {
476     return macerror;
477   }
478
479   struct ifaddrs* ifAddrs;
480   if (getifaddrs(&ifAddrs) == -1) return macerror;
481
482   char* ifName = NULL;
483
484   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
485   {
486     if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
487
488     if (sap->sa_family == AF_INET)
489     {
490       struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
491       if (test->sin_addr.s_addr == my4AddrU32)
492       {
493         ifName = ifa->ifa_name;
494         break;
495       }
496     }
497     else if (sap->sa_family == AF_INET6)
498     {
499       struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
500       if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
501       {
502         ifName = ifa->ifa_name;
503         break;
504       }
505     }
506   }
507
508   if (!ifName)
509   {
510     freeifaddrs(ifAddrs);
511     return macerror;
512   }
513
514   // Walk the list again to get the MAC for ifName using family == AF_PACKET
515   MACAddress toReturn = macerror;
516
517   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
518   {
519     if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
520     if (strcmp(ifName, ifa->ifa_name)) continue;
521     struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
522     if (sall->sll_halen != 6) continue;
523
524     memcpy(&toReturn, sall->sll_addr, 6);
525     break;
526   }
527
528   freeifaddrs(ifAddrs);
529   return toReturn;
530
531 #else // WIN32
532   PIP_ADAPTER_INFO daptinfo = NULL;
533   DWORD size = 0;
534   GetAdaptersInfo(daptinfo, &size);
535   daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
536   MACAddress macresult;
537   memcpy(&macresult, "ABCDEF", 6);//Dummy Address
538   sockaddr_in sock_address;
539   int sockname_len = sizeof(sock_address);
540   getsockname(sockfd, (sockaddr*)&sock_address, &sockname_len);
541   ULONG sockip = sock_address.sin_addr.s_addr;
542   if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
543   {
544     PIP_ADAPTER_INFO daptinfo_it = daptinfo;
545     while (daptinfo_it != NULL)
546     {
547       ULONG ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
548       if (ipaddress == sockip)
549       { //Is it our MAC?
550         memcpy(&macresult, daptinfo_it->Address, 6);
551         break;
552       }
553       daptinfo_it = daptinfo_it->Next;
554       if (daptinfo_it == daptinfo) break;
555     }
556   }
557   delete[] daptinfo;
558   return macresult;
559 #endif
560 }