]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
Windows fixes
[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 #ifdef WIN32
127   if ((connectResult != SOCKET_ERROR) || (WSAGetLastError() != WSAEWOULDBLOCK))
128 #else
129   if (errno != EINPROGRESS)
130 #endif
131   {
132     CLOSESOCKET(sockfd);
133     sockfd = -1;
134     logger->log("TCP", Log::CRIT, "connect error");
135     return false;
136   }
137
138   // Wait for connect to complete
139   fd_set writefds;
140   struct timeval tv;
141   FD_ZERO(&writefds);
142   FD_SET(sockfd, &writefds);
143   int maxfd = sockfd;
144
145 #ifdef WIN32
146   FD_SET(abortSocket, &writefds);
147 #else
148   FD_SET(abortPipe[0], &writefds);
149   if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
150 #endif
151
152   tv.tv_sec = 5; // Allow 5s for a connect
153   tv.tv_usec = 0;
154
155   int selectResult = select(maxfd + 1, NULL, &writefds, NULL, &tv);
156
157   if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
158   {
159     freeaddrinfo(aip);
160     logger->log("TCP", Log::INFO, "connected");
161     connected = true;
162     return true;
163   }
164   else
165   {
166     CLOSESOCKET(sockfd);
167     sockfd = -1;
168     logger->log("TCP", Log::CRIT, "connect/select error");
169     return false;
170   }
171 }
172
173 void TCP::shutdown()
174 {
175   if (!connected) return;
176   connected = false;
177   CLOSESOCKET(sockfd);
178   sockfd = -1;
179 }
180
181 bool TCP::status()
182 {
183   return connected;
184 }
185
186 void TCP::abortCall()
187 {
188 #ifdef WIN32
189   CLOSESOCKET(abortSocket);
190 #else
191   ::write(abortPipe[1], "X", 1);
192 #endif
193 }
194
195 bool TCP::write(void* src, ULONG numBytes)
196 {
197   if (!connected) return false;
198
199   std::lock_guard<std::mutex> lg(writeMutex);
200   int result = send(sockfd, reinterpret_cast<char*>(src), numBytes, 0);  // FIXME does send return < numBytes? Might need loop
201   if (result < 0) return false;
202   if (static_cast<ULONG>(result) != numBytes) return false;
203
204   return true;
205 }
206
207 bool TCP::read(void* dst, ULONG numBytes)
208 {
209   if (!connected) return false;
210
211   fd_set readfds;
212   struct timeval tv;
213
214   ULONG totalReceived = 0;
215   int abortCount = 0;
216   char* pointer = static_cast<char*>(dst); // WIN32 requires char*
217   
218   do
219   {
220     if (abortCount == 5)
221     {
222       logger->log("TCP", Log::DEBUG, "abortCount = 5");
223       return false;
224     }
225     
226     FD_ZERO(&readfds);
227     FD_SET(sockfd, &readfds);
228     int maxfd = sockfd; // WIN32 ignores
229
230 #ifdef WIN32
231     FD_SET(abortSocket, &readfds);
232 #else
233     FD_SET(abortPipe[0], &readfds);
234     if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
235 #endif
236
237     tv.tv_sec = 2;
238     tv.tv_usec = 0;
239
240     int selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
241
242     if (selectResult == -1) { shutdown(); return false; }
243     if (selectResult == 0) return false;
244
245 #ifdef WIN32
246   if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
247 #else
248   if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
249 #endif
250   
251     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
252     if (recvResult == -1) { shutdown(); return false; }
253     totalReceived += recvResult;
254     pointer += recvResult;
255     
256   } while (totalReceived < numBytes);
257   
258   return true;
259 }
260
261 MACAddress TCP::getMAC()
262 {
263 #ifndef WIN32
264   MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
265   if (!connected) return macerror;
266
267   /*
268   struct sockaddr      - man 2 bind
269   struct sockaddr_in   - man 7 ip
270   struct sockaddr_in6  - man 7 ipv6
271   struct sockaddr_ll   - man 7 packet
272   */
273
274   // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
275   UINT buflen = 50;
276   unsigned char gsnBuffer[buflen];
277   struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
278   if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
279
280   uint32_t my4AddrU32;
281   unsigned char my6Addr[16];
282
283   if (sap->sa_family == AF_INET)
284   {
285     struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
286     my4AddrU32 = sap4->sin_addr.s_addr;
287   }
288   else if (sap->sa_family == AF_INET6)
289   {
290     struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
291     memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
292   }
293   else
294   {
295     return macerror;
296   }
297
298   struct ifaddrs* ifAddrs;
299   if (getifaddrs(&ifAddrs) == -1) return macerror;
300
301   char* ifName = NULL;
302
303   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
304   {
305     if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
306
307     if (sap->sa_family == AF_INET)
308     {
309       struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
310       if (test->sin_addr.s_addr == my4AddrU32)
311       {
312         ifName = ifa->ifa_name;
313         break;
314       }
315     }
316     else if (sap->sa_family == AF_INET6)
317     {
318       struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
319       if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
320       {
321         ifName = ifa->ifa_name;
322         break;
323       }
324     }
325   }
326
327   if (!ifName)
328   {
329     freeifaddrs(ifAddrs);
330     return macerror;
331   }
332
333   // Walk the list again to get the MAC for ifName using family == AF_PACKET
334   MACAddress toReturn = macerror;
335
336   for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
337   {
338     if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
339     if (strcmp(ifName, ifa->ifa_name)) continue;
340     struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
341     if (sall->sll_halen != 6) continue;
342
343     memcpy(&toReturn, sall->sll_addr, 6);
344     break;
345   }
346
347   freeifaddrs(ifAddrs);
348   return toReturn;
349
350 #else // WIN32
351   PIP_ADAPTER_INFO daptinfo = NULL;
352   DWORD size = 0;
353   GetAdaptersInfo(daptinfo, &size);
354   daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
355   MACAddress macresult;
356   memcpy(&macresult, "ABCDEF", 6);//Dummy Address
357   sockaddr_in sock_address;
358   int sockname_len = sizeof(sock_address);
359   getsockname(sockfd, (sockaddr*)&sock_address, &sockname_len);
360   ULONG sockip = sock_address.sin_addr.s_addr;
361   if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
362   {
363     PIP_ADAPTER_INFO daptinfo_it = daptinfo;
364     while (daptinfo_it != NULL)
365     {
366       ULONG ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
367       if (ipaddress == sockip)
368       { //Is it our MAC?
369         memcpy(&macresult, daptinfo_it->Address, 6);
370         break;
371       }
372       daptinfo_it = daptinfo_it->Next;
373       if (daptinfo_it == daptinfo) break;
374     }
375   }
376   delete[] daptinfo;
377   return macresult;
378 #endif
379 }