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