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