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