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