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