]> git.vomp.tv Git - vompclient.git/blob - vdpc.cc
Makefile fixes
[vompclient.git] / vdpc.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 #ifndef WIN32
21   #include <unistd.h>
22   #include <fcntl.h>
23 #endif
24
25 #include <cstdlib>
26 #include <chrono>
27 #include <algorithm>
28
29 #include "log.h"
30 #include "wol.h"
31
32 #include "vdpc.h"
33
34 static const char* TAG = "VDPC";
35
36 bool VDPC::init()
37 {
38   AddServerCallback addFunc( [this] (int ipVersion, const char* ip, const char* name, USHORT port, ULONG version)
39   {
40     std::lock_guard<std::mutex> lg(serversLock);
41
42     if (dedupServers)
43     {
44       UINT i = 0;
45       for (; i < servers.size(); i++)
46       {
47         if (servers[i].name == name) break;
48       }
49
50       if (i == servers.size())
51       {
52         servers.emplace_back(ipVersion, ip, name, port, version);
53       }
54       else
55       {
56         if (ipVersion == preferIPV) // delete the other
57         {
58           servers[i].ipVersion = ipVersion;
59           servers[i].ip = ip;
60           servers[i].name = name;
61           servers[i].port = port;
62           servers[i].version = version;
63         }
64       }
65
66     }
67     else
68     {
69       servers.emplace_back(ipVersion, ip, name, port, version);
70     }
71   });
72
73 #ifdef IPV4
74   if (!vdpc4.init(addFunc)) return false;
75 #endif
76 #ifdef IPV6
77   if (!vdpc6.init(addFunc)) return false;
78 #endif
79
80   return true;
81 }
82
83 int VDPC::go()
84 {
85   std::unique_lock<std::mutex> ul(waitMutex);
86
87   servers.clear();
88
89 #ifdef IPV4
90   vdpc4.run();
91 #endif
92 #ifdef IPV6
93   vdpc6.run();
94 #endif
95
96   do
97   {
98     LogNT::getInstance()->debug(TAG, "Sending broadcasts");
99
100 #ifdef IPV4
101     vdpc4.sendRequest();
102 #endif
103 #ifdef IPV6
104     vdpc6.sendRequest();
105 #endif
106
107     waitCond.wait_for(ul, std::chrono::milliseconds(1500), [this]{ return stopNow == true; });
108     LogNT::getInstance()->debug(TAG, "go() wait finished");
109
110     if (stopNow) break;
111     if (servers.size() == 0) Wol::getInstance()->doWakeUp();
112
113   } while(servers.size() < 1);
114
115 #ifdef IPV4
116     vdpc4.stop();
117 #endif
118 #ifdef IPV6
119     vdpc6.stop();
120 #endif
121
122   std::sort(servers.begin(), servers.end(), ServerSorter(preferIPV));
123   return servers.size();
124 }
125
126 void VDPC::stop()
127 {
128   std::lock_guard<std::mutex> lg(waitMutex);
129   stopNow = true;
130   waitCond.notify_one();
131 }
132
133 ULONG VDPC::numServers() const
134 {
135   return servers.size();
136 }
137
138 const VDRServer& VDPC::operator[](ULONG index) const
139 {
140   if (index >= servers.size()) std::abort();
141   return servers[index];
142 }
143
144 #ifdef IPV4
145
146 // ======================================= VDPC4
147
148 bool VDPC::VDPC4::init(AddServerCallback& taddFunc)
149 {
150   LogNT::getInstance()->debug("VDPC4", "init");
151   addFunc = taddFunc;
152
153   if (!udp4.init(0))
154   {
155     LogNT::getInstance()->crit("VDPC4", "Failed to init VDPC4 UDP");
156     return false;
157   }
158
159   return true;
160 }
161
162 void VDPC::VDPC4::run()
163 {
164   LogNT::getInstance()->debug("VDPC4", "run");
165   stopNow = false;
166
167   startProtect.lock();
168   receiveThread = std::thread([this]
169   {
170     startProtect.lock();
171     startProtect.unlock();
172     threadMethod();
173   });
174   startProtect.unlock();
175 }
176
177 void VDPC::VDPC4::stop()
178 {
179   LogNT::getInstance()->debug("VDPC4", "stop");
180
181   stopNow = true;
182
183 #ifdef WIN32
184   CLOSESOCKET(quitPipe);
185 #else
186   write(pfdsUDP4[1], "X", 1);
187 #endif
188
189   receiveThread.join();
190
191 #ifndef WIN32
192   close(pfdsUDP4[0]);
193   close(pfdsUDP4[1]);
194 #endif
195 }
196
197 void VDPC::VDPC4::threadMethod()
198 {
199   LogNT* logger = LogNT::getInstance();
200
201 #ifdef WIN32
202   quitPipe = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
203   if (quitPipe == INVALID_SOCKET)
204   {
205     logger->error("VDPC4", "socket() fail");
206     return;
207   }
208 #else
209   if (pipe2(pfdsUDP4, O_NONBLOCK) == -1)
210   {
211     logger->error("VDPC4", "pipe2 error B");
212     return;
213   }
214 #endif
215
216   while (!stopNow)
217   {
218     logger->debug("VDPC4", "goto waitformessage");
219
220     #ifdef WIN32
221     UCHAR retval = udp4.waitforMessage(3, quitPipe);
222     #else
223     UCHAR retval = udp4.waitforMessage(3, pfdsUDP4[0]);
224     #endif
225     logger->debug("VDPC4", "backfrom waitformessage");
226
227     if (retval == 2) // we got a reply
228     {
229       const char* vdpreply = static_cast<const char*>(udp4.getData());
230       if ((udp4.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
231       {
232         // FIXME upgrade this to look for a return IP in the reply packet
233
234         USHORT newServerPort;
235         memcpy(&newServerPort, &vdpreply[26], 2);
236
237         ULONG newServerVersion;
238         memcpy(&newServerVersion, &vdpreply[28], 4);
239
240         // FIXME - packet length > 24 not checked, end NULL not checked!!
241         addFunc(4, udp4.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
242       }
243     }
244
245     logger->debug("VDPC4", "loop stopnow = %i", stopNow);
246   }
247 }
248
249 void VDPC::VDPC4::sendRequest()
250 {
251   LogNT::getInstance()->debug("VDPC4", "broadcast");
252
253   char message[15];
254   memset(message, 0, 15);
255   strcpy(message, "VDP-0001");
256   /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
257
258   udp4.send("255.255.255.255", 51051U, message, 15);
259   udp4.send("255.255.255.255", 51052U, message, 15);
260   udp4.send("255.255.255.255", 51053U, message, 15);
261   udp4.send("255.255.255.255", 51054U, message, 15);
262   udp4.send("255.255.255.255", 51055U, message, 15);
263 }
264
265 #endif
266
267 #ifdef IPV6
268
269 // ======================================= VDPC6
270
271 bool VDPC::VDPC6::init(AddServerCallback& taddFunc)
272 {
273   LogNT::getInstance()->debug("VDPC6", "init");
274   addFunc = taddFunc;
275
276   if (!udp6.init(0))
277   {
278     LogNT::getInstance()->crit("VDPC6", "Failed to init VDPC6 UDP");
279     return false;
280   }
281
282   return true;
283 }
284
285 void VDPC::VDPC6::run()
286 {
287   LogNT::getInstance()->debug("VDPC6", "run");
288   stopNow = false;
289
290   startProtect.lock();
291   receiveThread = std::thread([this]
292   {
293     startProtect.lock();
294     startProtect.unlock();
295     threadMethod();
296   });
297   startProtect.unlock();
298 }
299
300 void VDPC::VDPC6::stop()
301 {
302   LogNT::getInstance()->debug("VDPC6", "stop");
303
304   stopNow = true;
305   write(pfdsUDP6[1], "X", 1);
306   receiveThread.join();
307
308   close(pfdsUDP6[0]);
309   close(pfdsUDP6[1]);
310 }
311
312 void VDPC::VDPC6::threadMethod()
313 {
314   LogNT* logger = LogNT::getInstance();
315
316   if (pipe2(pfdsUDP6, O_NONBLOCK) == -1)
317   {
318     logger->error("VDPC6", "pipe2 error B");
319     return;
320   }
321
322   while (!stopNow)
323   {
324     logger->debug("VDPC6", "goto waitformessage");
325
326     UCHAR retval = udp6.waitforMessage(3, pfdsUDP6[0]);
327     logger->debug("VDPC6", "backfrom waitformessage");
328
329     if (retval == 2) // we got a reply
330     {
331       const char* vdpreply = static_cast<const char*>(udp6.getData());
332       if ((udp6.getDataLength() >= 24) && !strncmp(vdpreply, "VDP-0002", 8))
333       {
334         // FIXME upgrade this to look for a return IP in the reply packet
335
336         USHORT newServerPort;
337         memcpy(&newServerPort, &vdpreply[26], 2);
338
339         ULONG newServerVersion;
340         memcpy(&newServerVersion, &vdpreply[28], 4);
341
342         // FIXME - packet length > 24 not checked, end NULL not checked!!
343         addFunc(6, udp6.getFromIPA(), &vdpreply[32], ntohs(newServerPort), ntohl(newServerVersion));
344       }
345     }
346
347     logger->debug("VDPC6", "loop stopnow = %i", stopNow);
348   }
349 }
350
351 void VDPC::VDPC6::sendRequest()
352 {
353   LogNT::getInstance()->debug("VDPC6", "broadcast");
354
355   char message[15];
356   memset(message, 0, 15);
357   strcpy(message, "VDP-0001");
358   /*tcp->getMAC(&message[9]); put mac here when TCP modified to do this*/
359
360   udp6.send("ff15:766f:6d70:2064:6973:636f:7665:7279", 51056U, message, 15, true);
361 }
362
363 #endif