]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
Mods for Windows
[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
40 #include "defines.h"
41 #include "log.h"
42
43 #include "tcp.h"
44
45 TCP::~TCP()
46 {
47   shutdown();
48   
49 #ifdef WIN32
50   CLOSESOCKET(abortSocket);
51 #else
52   if (abortPipe[0] != -1)
53   {
54     close(abortPipe[0]);
55     close(abortPipe[1]);
56     abortPipe[0] = -1;
57     abortPipe[1] = -1;
58   }
59 #endif
60 }
61
62 bool TCP::init()
63 {
64   logger = Log::getInstance();
65   
66   #ifdef WIN32
67   abortSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
68   if (abortSocket == INVALID_SOCKET)
69   {
70     logger->log("TCP", Log::CRIT, "socket error");
71     return false;
72   }
73   #else
74   if (pipe2(abortPipe, O_NONBLOCK) == -1)
75   {
76     logger->log("TCP", Log::CRIT, "pipe2 error");
77     return false;
78   }
79   #endif
80   
81   return true;
82 }
83
84 bool TCP::connect(const std::string& ip, USHORT port)
85 {
86   if (connected) return false;
87
88   struct addrinfo hints;
89   memset(&hints, 0, sizeof(struct addrinfo));
90   hints.ai_family = AF_UNSPEC;
91   hints.ai_socktype = SOCK_STREAM;
92   hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
93
94   struct addrinfo* aip;
95   char portString[10];
96   SNPRINTF(portString, 10, "%u", port);
97   int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
98
99   if ((gaiResult != 0) || !aip)
100   {
101     logger->log("TCP", Log::CRIT, "getaddrinfo error");
102     return false;
103   }
104
105   sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
106   if (sockfd == -1) { logger->log("TCP", Log::CRIT, "socket error"); return false; }
107
108 #ifdef WIN32
109   ULONG param = 1;
110   ioctlsocket(sockfd, FIONBIO, &param);
111 #else
112   fcntl(sockfd, F_SETFL, O_NONBLOCK);
113 #endif
114
115   errno = 0;
116   // There should only be one aip result..
117   int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
118
119   if (connectResult == 0) // success
120   {
121     freeaddrinfo(aip);
122     connected = true;
123     return true;
124   }
125
126   if (errno != EINPROGRESS)
127   {
128     CLOSESOCKET(sockfd);
129     sockfd = -1;
130     logger->log("TCP", Log::CRIT, "connect error");
131     return false;
132   }
133
134   // Wait for connect to complete
135   fd_set writefds;
136   struct timeval tv;
137   FD_ZERO(&writefds);
138   FD_SET(sockfd, &writefds);
139   int maxfd = sockfd;
140
141 #ifdef WIN32
142   FD_SET(abortSocket, &writefds);
143 #else
144   FD_SET(abortPipe[0], &writefds);
145   if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
146 #endif
147
148   tv.tv_sec = 5; // Allow 5s for a connect
149   tv.tv_usec = 0;
150
151   int selectResult = select(maxfd + 1, NULL, &writefds, NULL, &tv);
152
153   if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
154   {
155     freeaddrinfo(aip);
156     logger->log("TCP", Log::INFO, "connected");
157     connected = true;
158     return true;
159   }
160   else
161   {
162     CLOSESOCKET(sockfd);
163     sockfd = -1;
164     logger->log("TCP", Log::CRIT, "connect/select error");
165     return false;
166   }
167 }
168
169 void TCP::shutdown()
170 {
171   if (!connected) return;
172   connected = false;
173   CLOSESOCKET(sockfd);
174   sockfd = -1;
175 }
176
177 bool TCP::status()
178 {
179   return connected;
180 }
181
182 void TCP::abortCall()
183 {
184 #ifdef WIN32
185   CLOSESOCKET(abortSocket);
186 #else
187   ::write(abortPipe[1], "X", 1);
188 #endif
189 }
190
191 bool TCP::write(void* src, ULONG numBytes)
192 {
193   if (!connected) return false;
194
195   std::lock_guard<std::mutex> lg(writeMutex);
196   int result = send(sockfd, reinterpret_cast<char*>(src), numBytes, 0);  // FIXME does send return < numBytes? Might need loop
197   if (result < 0) return false;
198   if (static_cast<ULONG>(result) != numBytes) return false;
199
200   return true;
201 }
202
203 bool TCP::read(void* dst, ULONG numBytes)
204 {
205   if (!connected) return false;
206
207   fd_set readfds;
208   struct timeval tv;
209
210   ULONG totalReceived = 0;
211   int abortCount = 0;
212   char* pointer = static_cast<char*>(dst); // WIN32 requires char*
213   
214   do
215   {
216     if (abortCount == 5)
217     {
218       logger->log("TCP", Log::DEBUG, "abortCount = 5");
219       return false;
220     }
221     
222     FD_ZERO(&readfds);
223     FD_SET(sockfd, &readfds);
224     int maxfd = sockfd; // WIN32 ignores
225
226 #ifdef WIN32
227     FD_SET(abortSocket, &readfds);
228 #else
229     FD_SET(abortPipe[0], &readfds);
230     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
231 #endif
232
233     tv.tv_sec = 2;
234     tv.tv_usec = 0;
235
236     int selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
237
238     if (selectResult == -1) { shutdown(); return false; }
239     if (selectResult == 0) return false;
240
241 #ifdef WIN32
242   if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
243 #else
244   if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
245 #endif
246   
247     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
248     if (recvResult == -1) { shutdown(); return false; }
249     totalReceived += recvResult;
250     pointer += recvResult;
251     
252   } while (totalReceived < numBytes);
253   
254   return true;
255 }
256
257 MACAddress TCP::getMAC()
258 {
259 #ifndef WIN32
260   MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
261   if (!connected) return macerror;
262
263   /*
264   struct sockaddr      - man 2 bind
265   struct sockaddr_in   - man 7 ip
266   struct sockaddr_in6  - man 7 ipv6
267   struct sockaddr_ll   - man 7 packet
268   */
269
270   // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
271   UINT buflen = 50;
272   unsigned char gsnBuffer[buflen];
273   struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
274   if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
275
276   uint32_t my4AddrU32;
277   unsigned char my6Addr[16];
278
279   if (sap->sa_family == AF_INET)
280   {
281     struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
282     my4AddrU32 = sap4->sin_addr.s_addr;
283   }
284   else if (sap->sa_family == AF_INET6)
285   {
286     struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
287     memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
288   }
289   else
290   {
291     return macerror;
292   }
293
294   struct ifaddrs* ifAddrs;
295   if (getifaddrs(&ifAddrs) == -1) return macerror;
296
297   char* ifName = NULL;
298
299   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
300   {
301     if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
302
303     if (sap->sa_family == AF_INET)
304     {
305       struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
306       if (test->sin_addr.s_addr == my4AddrU32)
307       {
308         ifName = ifa->ifa_name;
309         break;
310       }
311     }
312     else if (sap->sa_family == AF_INET6)
313     {
314       struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
315       if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
316       {
317         ifName = ifa->ifa_name;
318         break;
319       }
320     }
321   }
322
323   if (!ifName)
324   {
325     freeifaddrs(ifAddrs);
326     return macerror;
327   }
328
329   // Walk the list again to get the MAC for ifName using family == AF_PACKET
330   MACAddress toReturn = macerror;
331
332   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
333   {
334     if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
335     if (strcmp(ifName, ifa->ifa_name)) continue;
336     struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
337     if (sall->sll_halen != 6) continue;
338
339     memcpy(&toReturn, sall->sll_addr, 6);
340     break;
341   }
342
343   freeifaddrs(ifAddrs);
344   return toReturn;
345
346 #else // WIN32
347   PIP_ADAPTER_INFO daptinfo = NULL;
348   DWORD size = 0;
349   GetAdaptersInfo(daptinfo, &size);
350   daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
351   MACAddress macresult;
352   memcpy(&macresult, "ABCDEF", 6);//Dummy Address
353   sockaddr_in sock_address;
354   int sockname_len = sizeof(sock_address);
355   getsockname(sockfd, (sockaddr*)&sock_address, &sockname_len);
356   ULONG sockip = sock_address.sin_addr.s_addr;
357   if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
358   {
359     PIP_ADAPTER_INFO daptinfo_it = daptinfo;
360     while (daptinfo_it != NULL)
361     {
362       ULONG ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
363       if (ipaddress == sockip)
364       { //Is it our MAC?
365         memcpy(&macresult, daptinfo_it->Address, 6);
366         break;
367       }
368       daptinfo_it = daptinfo_it->Next;
369       if (daptinfo_it == daptinfo) break;
370     }
371   }
372   delete[] daptinfo;
373   return macresult;
374 #endif
375 }