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/>.
34 static const char* TAG = "VDPC";
38 AddServerCallback addFunc( [this] (int ipVersion, const char* ip, const char* name, USHORT port, ULONG version)
40 std::lock_guard<std::mutex> lg(serversLock);
42 for (auto& s : servers)
44 if (!s.ip.compare(ip) && (port == s.port)) return;
47 servers.emplace_back(ipVersion, ip, name, port, version);
51 if (!vdpc4.init(addFunc)) return false;
54 if (!vdpc6.init(addFunc)) return false;
62 std::unique_lock<std::mutex> ul(waitMutex);
75 LogNT::getInstance()->debug(TAG, "Sending broadcasts");
84 waitCond.wait_for(ul, std::chrono::milliseconds(1500), [this]{ return stopNow == true; });
85 LogNT::getInstance()->debug(TAG, "go() wait finished");
88 if (servers.size() == 0) Wol::getInstance()->doWakeUp();
90 } while(servers.size() < 1);
99 std::sort(servers.begin(), servers.end(), ServerSorter());
100 return servers.size();
105 std::lock_guard<std::mutex> lg(waitMutex);
107 waitCond.notify_one();
110 ULONG VDPC::numServers() const
112 return servers.size();
115 const VDRServer& VDPC::operator[](ULONG index) const
117 if (index >= servers.size()) std::abort();
118 return servers[index];
123 // ======================================= VDPC4
125 bool VDPC::VDPC4::init(AddServerCallback& taddFunc)
127 LogNT::getInstance()->debug("VDPC4", "init");
132 LogNT::getInstance()->crit("VDPC4", "Failed to init VDPC4 UDP");
139 void VDPC::VDPC4::run()
141 LogNT::getInstance()->debug("VDPC4", "run");
145 receiveThread = std::thread([this]
148 startProtect.unlock();
151 startProtect.unlock();
154 void VDPC::VDPC4::stop()
156 LogNT::getInstance()->debug("VDPC4", "stop");
161 CLOSESOCKET(quitPipe);
163 write(pfdsUDP4[1], "X", 1);
166 receiveThread.join();
174 void VDPC::VDPC4::threadMethod()
176 LogNT* logger = LogNT::getInstance();
179 quitPipe = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
180 if (quitPipe == INVALID_SOCKET)
182 logger->error("VDPC4", "socket() fail");
186 if (pipe2(pfdsUDP4, O_NONBLOCK) == -1)
188 logger->error("VDPC4", "pipe2 error B");
195 logger->debug("VDPC4", "goto waitformessage");
198 UCHAR retval = udp4.waitforMessage(3, quitPipe);
200 UCHAR retval = udp4.waitforMessage(3, pfdsUDP4[0]);
202 logger->debug("VDPC4", "backfrom waitformessage");
204 if (retval == 2) // we got a reply
206 const char* vdpreply = static_cast<const char*>(udp4.getData());
207 if ((udp4.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
209 // FIXME upgrade this to look for a return IP in the reply packet
211 USHORT newServerPort;
212 memcpy(&newServerPort, &vdpreply[26], 2);
214 ULONG newServerVersion;
215 memcpy(&newServerVersion, &vdpreply[28], 4);
217 // FIXME - packet length > 24 not checked, end NULL not checked!!
218 addFunc(4, udp4.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
222 logger->debug("VDPC4", "loop stopnow = {}", stopNow);
226 void VDPC::VDPC4::sendRequest()
228 LogNT::getInstance()->debug("VDPC4", "broadcast");
231 memset(message, 0, 15);
232 strcpy(message, "VDP-0001");
233 /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
235 udp4.send("255.255.255.255", 51051U, message, 15);
236 udp4.send("255.255.255.255", 51052U, message, 15);
237 udp4.send("255.255.255.255", 51053U, message, 15);
238 udp4.send("255.255.255.255", 51054U, message, 15);
239 udp4.send("255.255.255.255", 51055U, message, 15);
246 // ======================================= VDPC6
248 bool VDPC::VDPC6::init(AddServerCallback& taddFunc)
250 LogNT::getInstance()->debug("VDPC6", "init");
255 LogNT::getInstance()->crit("VDPC6", "Failed to init VDPC6 UDP");
262 void VDPC::VDPC6::run()
264 LogNT::getInstance()->debug("VDPC6", "run");
268 receiveThread = std::thread([this]
271 startProtect.unlock();
274 startProtect.unlock();
277 void VDPC::VDPC6::stop()
279 LogNT::getInstance()->debug("VDPC6", "stop");
282 write(pfdsUDP6[1], "X", 1);
283 receiveThread.join();
289 void VDPC::VDPC6::threadMethod()
291 LogNT* logger = LogNT::getInstance();
293 if (pipe2(pfdsUDP6, O_NONBLOCK) == -1)
295 logger->error("VDPC6", "pipe2 error B");
301 logger->debug("VDPC6", "goto waitformessage");
303 UCHAR retval = udp6.waitforMessage(3, pfdsUDP6[0]);
304 logger->debug("VDPC6", "backfrom waitformessage");
306 if (retval == 2) // we got a reply
308 const char* vdpreply = static_cast<const char*>(udp6.getData());
309 if ((udp6.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
311 if (udp6.getFromIPisLL())
313 // Reject responses from link local addresses
314 // To use them the incoming interface name would need to be kept for use in the tcp connect
315 // This would be possible but there doesn't seem to be any point, there
316 // are a few spare real or ULA IPv6 addresses left
317 // Though it would be cool for it to just-work on an unconfigured IPv6 system...
320 logger->debug("VDPC6", "Rejecting response from a link-local address");
324 // FIXME upgrade this to look for a return IP in the reply packet
326 USHORT newServerPort;
327 memcpy(&newServerPort, &vdpreply[26], 2);
329 ULONG newServerVersion;
330 memcpy(&newServerVersion, &vdpreply[28], 4);
332 // FIXME - packet length > 24 not checked, end NULL not checked!!
333 addFunc(6, udp6.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
338 logger->debug("VDPC6", "loop stopnow = {}", stopNow);
342 void VDPC::VDPC6::sendRequest()
344 LogNT::getInstance()->debug("VDPC6", "broadcast");
347 memset(message, 0, 15);
348 strcpy(message, "VDP-0001");
349 /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
351 udp6.send("ff15:766f:6d70:2064:6973:636f:7665:7279", 51056U, message, 15, true);