2 Copyright 2020 Chris Tallon
4 This file is part of VOMP.
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.
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.
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/>.
20 #include <string.h> // memcmp
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
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
49 static const char* TAG = "TCP";
56 CLOSESOCKET(abortSocket);
58 if (abortPipe[0] != -1)
70 logger = LogNT::getInstance();
73 abortSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
74 if (abortSocket == INVALID_SOCKET)
76 logger->crit(TAG, "socket error");
80 if (pipe2(abortPipe, O_NONBLOCK) == -1)
82 logger->crit(TAG, "pipe2 error");
91 bool TCP::connectSocket(const std::string& socketFile)
93 if (connected) return false;
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; }
98 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
99 if (sockfd == -1) { logger->crit(TAG, "socket error"); return false; }
101 fcntl(sockfd, F_SETFL, O_NONBLOCK);
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));
108 if (connectResult == 0) // success
114 return waitForConnect(connectResult);
118 bool TCP::connect(const std::string& ip, u2 port)
120 if (connected) return false;
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;
128 struct addrinfo* aip;
130 SNPRINTF(portString, 10, "%u", port);
131 int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
133 if ((gaiResult != 0) || !aip)
135 logger->crit(TAG, "getaddrinfo error");
139 sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
140 if (sockfd == -1) { logger->crit(TAG, "socket error"); return false; }
144 ioctlsocket(sockfd, FIONBIO, ¶m);
146 fcntl(sockfd, F_SETFL, O_NONBLOCK);
150 // There should only be one aip result..
151 int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
155 if (connectResult == 0) // success
161 return waitForConnect(connectResult);
165 bool TCP::waitForConnect(int connectResult)
167 if ((connectResult != SOCKET_ERROR) || (WSAGetLastError() != WSAEWOULDBLOCK))
169 bool TCP::waitForConnect(int)
171 if (errno != EINPROGRESS)
176 logger->crit(TAG, "connect error");
180 // Wait for connect to complete
183 FD_SET(sockfd, &writefds);
188 FD_SET(sockfd, &readfds);
192 FD_SET(abortSocket, &readfds);
194 FD_SET(abortPipe[0], &readfds);
195 if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
199 tv.tv_sec = 10; // Allow 10s for a connect
202 int selectResult = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
204 if (FD_ISSET(abortSocket, &readfds))
206 if (FD_ISSET(abortPipe[0], &readfds))
211 logger->info(TAG, "connect/select aborting");
215 if ((selectResult == 1) && FD_ISSET(sockfd, &writefds))
217 logger->info(TAG, "Connected");
221 else if ((selectResult == 2) && FD_ISSET(sockfd, &readfds) && FD_ISSET(sockfd, &writefds))
225 logger->crit(TAG, "Detected connection failed");
232 logger->crit(TAG, "connect/select error");
239 if (!connected) return;
246 while (recv(abortSocket, waste, 10, 0) > 0) ;
248 while (::read(abortPipe[0], waste, 10) > 0) ;
255 recStringBufStart = 0;
256 recStringBufUsed = 0;
265 void TCP::abortCall()
268 CLOSESOCKET(abortSocket);
270 ::write(abortPipe[1], "X", 1);
274 bool TCP::write(void* src, u4 numBytes)
276 if (!connected) return false;
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<u4>(result) != numBytes) return false;
286 bool TCP::read(void* dst, u4 numBytes, int timeoutSec)
288 if (!connected) return false;
293 u4 totalReceived = 0;
295 char* pointer = static_cast<char*>(dst); // WIN32 requires char*
299 if (++abortCount == 1000)
301 logger->error(TAG, "abortCount = 1000 - runaway error, or packet arrived in > 1000 pieces??");
306 FD_SET(sockfd, &readfds);
307 int maxfd = sockfd; // WIN32 ignores
310 FD_SET(abortSocket, &readfds);
312 FD_SET(abortPipe[0], &readfds);
313 if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
319 tv.tv_sec = timeoutSec;
321 selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
325 selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
328 if (selectResult == -1) { shutdown(); return false; }
329 if (selectResult == 0) return false;
332 if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
334 if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return false; }
337 int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
338 if (recvResult == -1) { shutdown(); return false; }
339 totalReceived += recvResult;
340 pointer += recvResult;
342 } while (totalReceived < numBytes);
347 std::stringstream TCP::readString(bool* result, int timeoutSec)
351 std::stringstream ss;
353 if (!connected) return ss;
360 if (!recStringBuf) recStringBuf = static_cast<char*>(malloc(recStringBufSize));
361 if (!recStringBuf) return ss;
363 // Absorb over-read from last time?
364 if (recStringBufUsed)
366 ss.write(&recStringBuf[recStringBufStart], recStringBufUsed);
367 recStringBufStart = 0;
368 recStringBufUsed = 0;
373 if (++abortCount == 20)
375 logger->debug(TAG, "abortCount = 20");
380 FD_SET(sockfd, &readfds);
381 int maxfd = sockfd; // WIN32 ignores
384 FD_SET(abortSocket, &readfds);
386 FD_SET(abortPipe[0], &readfds);
387 if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
393 tv.tv_sec = timeoutSec;
395 selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
399 selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
402 if (selectResult == -1) { shutdown(); return ss; }
403 if (selectResult == 0) return ss;
406 if (FD_ISSET(abortSocket, &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
408 if (FD_ISSET(abortPipe[0], &readfds)) { logger->debug(TAG, "Aborting..."); return ss; }
411 int recvResult = recv(sockfd, &recStringBuf[recStringBufUsed], recStringBufSize - recStringBufUsed, 0);
412 if (recvResult == -1) { shutdown(); return ss; }
413 recStringBufUsed += recvResult;
415 // Do we have a full string?
416 for(int i = 0; i < recStringBufUsed; i++)
418 if (recStringBuf[i] == '\n')
421 ss.write(recStringBuf, i);
423 if ((i + 1) != recStringBufUsed) // over read
425 i += 1; // Advance over \n
426 recStringBufStart = i;
427 recStringBufUsed -= i;
431 recStringBufUsed = 0;
438 // no \n in buffer, go around
442 MACAddress TCP::getMAC()
445 MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
446 if (!connected) return macerror;
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
455 // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
457 unsigned char gsnBuffer[buflen];
458 struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
459 if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
462 unsigned char my6Addr[16];
464 if (sap->sa_family == AF_INET)
466 struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
467 my4AddrU32 = sap4->sin_addr.s_addr;
469 else if (sap->sa_family == AF_INET6)
471 struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
472 memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
479 struct ifaddrs* ifAddrs;
480 if (getifaddrs(&ifAddrs) == -1) return macerror;
484 for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
486 if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
488 if (sap->sa_family == AF_INET)
490 struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
491 if (test->sin_addr.s_addr == my4AddrU32)
493 ifName = ifa->ifa_name;
497 else if (sap->sa_family == AF_INET6)
499 struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
500 if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
502 ifName = ifa->ifa_name;
510 freeifaddrs(ifAddrs);
514 // Walk the list again to get the MAC for ifName using family == AF_PACKET
515 MACAddress toReturn = macerror;
517 for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
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;
524 memcpy(&toReturn, sall->sll_addr, 6);
528 freeifaddrs(ifAddrs);
532 PIP_ADAPTER_INFO daptinfo = NULL;
534 GetAdaptersInfo(daptinfo, &size);
535 daptinfo = (PIP_ADAPTER_INFO)new char[size + 1];
536 MACAddress macresult;
537 memcpy(¯esult, "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 u4 sockip = sock_address.sin_addr.s_addr;
542 if (GetAdaptersInfo(daptinfo, &size) == ERROR_SUCCESS)
544 PIP_ADAPTER_INFO daptinfo_it = daptinfo;
545 while (daptinfo_it != NULL)
547 u4 ipaddress = inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
548 if (ipaddress == sockip)
550 memcpy(¯esult, daptinfo_it->Address, 6);
553 daptinfo_it = daptinfo_it->Next;
554 if (daptinfo_it == daptinfo) break;