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
21 #include <unistd.h> // pipe2
22 #include <fcntl.h> // pipe2-O_NONBLOCK fcntl
23 #include <sys/types.h> // socket connect getaddrinfo
24 #include <sys/socket.h> // socket connect getaddrinfo
25 #include <string.h> // memset
26 #include <netdb.h> // getaddrinfo
27 #include <sys/select.h> // select
28 #include <errno.h> // errno var
29 #include <ifaddrs.h> // getifaddrs
30 #include <linux/if_packet.h> // for getMAC
31 #include <net/ethernet.h> // for getMAC
41 if (abortPipe[0] != -1)
52 logger = Log::getInstance();
54 if (pipe2(abortPipe, O_NONBLOCK) == -1)
56 logger->log("TCP", Log::CRIT, "pipe2 error");
63 bool TCP::connect(const std::string& ip, USHORT port)
65 if (connected) return false;
67 struct addrinfo hints;
68 memset(&hints, 0, sizeof(struct addrinfo));
69 hints.ai_family = AF_UNSPEC;
70 hints.ai_socktype = SOCK_STREAM;
71 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
75 std::snprintf(portString, 10, "%u", port);
76 int gaiResult = getaddrinfo(ip.c_str(), portString, &hints, &aip);
78 if ((gaiResult != 0) || !aip)
80 logger->log("TCP", Log::CRIT, "getaddrinfo error");
84 sockfd = socket(aip->ai_family, SOCK_STREAM, 0);
85 if (sockfd == -1) { logger->log("TCP", Log::CRIT, "socket error"); return false; }
87 fcntl(sockfd, F_SETFL, O_NONBLOCK);
90 // There should only be one aip result..
91 int connectResult = ::connect(sockfd, aip->ai_addr, aip->ai_addrlen);
93 if (connectResult == 0) // success
100 if (errno != EINPROGRESS)
104 logger->log("TCP", Log::CRIT, "connect error");
108 // Wait for connect to complete
112 FD_SET(abortPipe[0], &writefds);
113 FD_SET(sockfd, &writefds);
115 tv.tv_sec = 5; // Allow 5s for a connect
118 int selectResult = select((sockfd > abortPipe[0] ? sockfd : abortPipe[0]) + 1, NULL, &writefds, NULL, &tv);
120 if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
123 logger->log("TCP", Log::INFO, "connected");
131 logger->log("TCP", Log::CRIT, "connect/select error");
138 if (!connected) return;
149 void TCP::abortCall()
151 ::write(abortPipe[1], "X", 1);
154 bool TCP::write(void* src, ULONG numBytes)
156 if (!connected) return false;
158 std::lock_guard<std::mutex> lg(writeMutex);
159 int result = send(sockfd, src, numBytes, 0); // FIXME does send return < numBytes? Might need loop
160 if (result < 0) return false;
161 if (static_cast<ULONG>(result) != numBytes) return false;
166 bool TCP::read(void* dst, ULONG numBytes)
168 if (!connected) return false;
173 ULONG totalReceived = 0;
175 UCHAR* pointer = static_cast<UCHAR*>(dst);
181 logger->log("TCP", Log::DEBUG, "abortCount = 5");
186 FD_SET(abortPipe[0], &readfds);
187 FD_SET(sockfd, &readfds);
192 int selectResult = select((sockfd > abortPipe[0] ? sockfd : abortPipe[0]) + 1, &readfds, NULL, NULL, &tv);
194 if (selectResult == -1) { shutdown(); return false; }
195 if (selectResult == 0) return false;
196 if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
198 int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
199 if (recvResult == -1) { shutdown(); return false; }
200 totalReceived += recvResult;
201 pointer += recvResult;
203 } while (totalReceived < numBytes);
208 MACAddress TCP::getMAC()
210 MACAddress macerror{ 00, 00, 00, 00, 00, 00 };
212 if (!connected) return macerror;
215 struct sockaddr - man 2 bind
216 struct sockaddr_in - man 7 ip
217 struct sockaddr_in6 - man 7 ipv6
218 struct sockaddr_ll - man 7 packet
221 // Measured sizeof(struct sockaddr_in) = 16, and in6 = 28
223 unsigned char gsnBuffer[buflen];
224 struct sockaddr* sap = reinterpret_cast<struct sockaddr*>(gsnBuffer);
225 if (getsockname(sockfd, sap, &buflen) == -1) return macerror;
228 unsigned char my6Addr[16];
230 if (sap->sa_family == AF_INET)
232 struct sockaddr_in* sap4 = reinterpret_cast<struct sockaddr_in*>(gsnBuffer);
233 my4AddrU32 = sap4->sin_addr.s_addr;
235 else if (sap->sa_family == AF_INET6)
237 struct sockaddr_in6* sap6 = reinterpret_cast<struct sockaddr_in6*>(gsnBuffer);
238 memcpy(my6Addr, sap6->sin6_addr.s6_addr, 16);
245 struct ifaddrs* ifAddrs;
246 if (getifaddrs(&ifAddrs) == -1) return macerror;
250 for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
252 if (ifa->ifa_addr->sa_family != sap->sa_family) continue;
254 if (sap->sa_family == AF_INET)
256 struct sockaddr_in* test = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
257 if (test->sin_addr.s_addr == my4AddrU32)
259 ifName = ifa->ifa_name;
263 else if (sap->sa_family == AF_INET6)
265 struct sockaddr_in6* test = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
266 if (!memcmp(my6Addr, test->sin6_addr.s6_addr, 16))
268 ifName = ifa->ifa_name;
276 freeifaddrs(ifAddrs);
280 // Walk the list again to get the MAC for ifName using family == AF_PACKET
281 MACAddress toReturn = macerror;
283 for(struct ifaddrs* ifa = ifAddrs; ifa != NULL; ifa = ifa->ifa_next)
285 if (ifa->ifa_addr->sa_family != AF_PACKET) continue;
286 if (strcmp(ifName, ifa->ifa_name)) continue;
287 struct sockaddr_ll* sall = reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
288 if (sall->sll_halen != 6) continue;
290 memcpy(&toReturn, sall->sll_addr, 6);
294 freeifaddrs(ifAddrs);