1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include <netdb.h>
10 #include <netinet/in.h>
11 #include <sys/socket.h>
12 #include <arpa/inet.h>
13
14 #include "Network.h"
15 #include "ServiceBroker.h"
16 #include "messaging/ApplicationMessenger.h"
17 #include "network/NetworkServices.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/log.h"
21 #ifdef TARGET_WINDOWS
22 #include "platform/win32/WIN32Util.h"
23 #include "utils/CharsetConverter.h"
24 #endif
25 #include "utils/StringUtils.h"
26 #include "utils/XTimeUtils.h"
27
28 using namespace KODI::MESSAGING;
29
30 /* slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) */
in_ether(const char * bufp,unsigned char * addr)31 bool in_ether (const char *bufp, unsigned char *addr)
32 {
33 if (strlen(bufp) != 17)
34 return false;
35
36 char c;
37 const char *orig;
38 unsigned char *ptr = addr;
39 unsigned val;
40
41 int i = 0;
42 orig = bufp;
43
44 while ((*bufp != '\0') && (i < 6))
45 {
46 val = 0;
47 c = *bufp++;
48
49 if (isdigit(c))
50 val = c - '0';
51 else if (c >= 'a' && c <= 'f')
52 val = c - 'a' + 10;
53 else if (c >= 'A' && c <= 'F')
54 val = c - 'A' + 10;
55 else
56 return false;
57
58 val <<= 4;
59 c = *bufp;
60 if (isdigit(c))
61 val |= c - '0';
62 else if (c >= 'a' && c <= 'f')
63 val |= c - 'a' + 10;
64 else if (c >= 'A' && c <= 'F')
65 val |= c - 'A' + 10;
66 else if (c == ':' || c == '-' || c == 0)
67 val >>= 4;
68 else
69 return false;
70
71 if (c != 0)
72 bufp++;
73
74 *ptr++ = (unsigned char) (val & 0377);
75 i++;
76
77 if (*bufp == ':' || *bufp == '-')
78 bufp++;
79 }
80
81 if (bufp - orig != 17)
82 return false;
83
84 return true;
85 }
86
CNetworkBase()87 CNetworkBase::CNetworkBase() :
88 m_services(new CNetworkServices())
89 {
90 CApplicationMessenger::GetInstance().PostMsg(TMSG_NETWORKMESSAGE, SERVICES_UP, 0);
91 }
92
~CNetworkBase()93 CNetworkBase::~CNetworkBase()
94 {
95 CApplicationMessenger::GetInstance().PostMsg(TMSG_NETWORKMESSAGE, SERVICES_DOWN, 0);
96 }
97
ParseHex(char * str,unsigned char * addr)98 int CNetworkBase::ParseHex(char *str, unsigned char *addr)
99 {
100 int len = 0;
101
102 while (*str)
103 {
104 int tmp;
105 if (str[1] == 0)
106 return -1;
107 if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1)
108 return -1;
109 addr[len] = tmp;
110 len++;
111 str += 2;
112 }
113
114 return len;
115 }
116
GetHostName(std::string & hostname)117 bool CNetworkBase::GetHostName(std::string& hostname)
118 {
119 char hostName[128];
120 if (gethostname(hostName, sizeof(hostName)))
121 return false;
122
123 #ifdef TARGET_WINDOWS
124 std::string hostStr;
125 g_charsetConverter.systemToUtf8(hostName, hostStr);
126 hostname = hostStr;
127 #else
128 hostname = hostName;
129 #endif
130 return true;
131 }
132
IsLocalHost(const std::string & hostname)133 bool CNetworkBase::IsLocalHost(const std::string& hostname)
134 {
135 if (hostname.empty())
136 return false;
137
138 if (StringUtils::StartsWith(hostname, "127.")
139 || (hostname == "::1")
140 || StringUtils::EqualsNoCase(hostname, "localhost"))
141 return true;
142
143 std::string myhostname;
144 if (GetHostName(myhostname)
145 && StringUtils::EqualsNoCase(hostname, myhostname))
146 return true;
147
148 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
149 std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
150 while (iter != ifaces.end())
151 {
152 CNetworkInterface* iface = *iter;
153 if (iface && iface->GetCurrentIPAddress() == hostname)
154 return true;
155
156 ++iter;
157 }
158
159 return false;
160 }
161
GetFirstConnectedInterface()162 CNetworkInterface* CNetworkBase::GetFirstConnectedInterface()
163 {
164 CNetworkInterface* fallbackInterface = nullptr;
165 for (CNetworkInterface* iface : GetInterfaceList())
166 {
167 if (iface && iface->IsConnected())
168 {
169 if (!iface->GetCurrentDefaultGateway().empty())
170 return iface;
171 else if (fallbackInterface == nullptr)
172 fallbackInterface = iface;
173 }
174 }
175
176 return fallbackInterface;
177 }
178
HasInterfaceForIP(unsigned long address)179 bool CNetworkBase::HasInterfaceForIP(unsigned long address)
180 {
181 unsigned long subnet;
182 unsigned long local;
183 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
184 std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
185 while (iter != ifaces.end())
186 {
187 CNetworkInterface* iface = *iter;
188 if (iface && iface->IsConnected())
189 {
190 subnet = ntohl(inet_addr(iface->GetCurrentNetmask().c_str()));
191 local = ntohl(inet_addr(iface->GetCurrentIPAddress().c_str()));
192 if( (address & subnet) == (local & subnet) )
193 return true;
194 }
195 ++iter;
196 }
197
198 return false;
199 }
200
IsAvailable(void)201 bool CNetworkBase::IsAvailable(void)
202 {
203 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
204 return (ifaces.size() != 0);
205 }
206
IsConnected()207 bool CNetworkBase::IsConnected()
208 {
209 return GetFirstConnectedInterface() != NULL;
210 }
211
NetworkMessage(EMESSAGE message,int param)212 void CNetworkBase::NetworkMessage(EMESSAGE message, int param)
213 {
214 switch( message )
215 {
216 case SERVICES_UP:
217 CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__);
218 m_services->Start();
219 break;
220
221 case SERVICES_DOWN:
222 CLog::Log(LOGDEBUG, "%s - Signaling network services to stop",__FUNCTION__);
223 m_services->Stop(false); // tell network services to stop, but don't wait for them yet
224 CLog::Log(LOGDEBUG, "%s - Waiting for network services to stop",__FUNCTION__);
225 m_services->Stop(true); // wait for network services to stop
226 break;
227 }
228 }
229
WakeOnLan(const char * mac)230 bool CNetworkBase::WakeOnLan(const char* mac)
231 {
232 int i, j, packet;
233 unsigned char ethaddr[8];
234 unsigned char buf [128];
235 unsigned char *ptr;
236
237 // Fetch the hardware address
238 if (!in_ether(mac, ethaddr))
239 {
240 CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac);
241 return false;
242 }
243
244 // Setup the socket
245 if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
246 {
247 CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno));
248 return false;
249 }
250
251 // Set socket options
252 struct sockaddr_in saddr;
253 saddr.sin_family = AF_INET;
254 saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
255 saddr.sin_port = htons(9);
256
257 unsigned int value = 1;
258 if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
259 {
260 CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno));
261 closesocket(packet);
262 return false;
263 }
264
265 // Build the magic packet (6 x 0xff + 16 x MAC address)
266 ptr = buf;
267 for (i = 0; i < 6; i++)
268 *ptr++ = 0xff;
269
270 for (j = 0; j < 16; j++)
271 for (i = 0; i < 6; i++)
272 *ptr++ = ethaddr[i];
273
274 // Send the magic packet
275 if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
276 {
277 CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno));
278 closesocket(packet);
279 return false;
280 }
281
282 closesocket(packet);
283 CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac);
284 return true;
285 }
286
287 // ping helper
ConnectHostPort(SOCKET soc,const struct sockaddr_in & addr,struct timeval & timeOut,bool tryRead)288 static const char* ConnectHostPort(SOCKET soc, const struct sockaddr_in& addr, struct timeval& timeOut, bool tryRead)
289 {
290 // set non-blocking
291 #ifdef TARGET_WINDOWS
292 u_long nonblocking = 1;
293 int result = ioctlsocket(soc, FIONBIO, &nonblocking);
294 #else
295 int result = fcntl(soc, F_SETFL, fcntl(soc, F_GETFL) | O_NONBLOCK);
296 #endif
297
298 if (result != 0)
299 return "set non-blocking option failed";
300
301 result = connect(soc, (const struct sockaddr *)&addr, sizeof(addr)); // non-blocking connect, will fail ..
302
303 if (result < 0)
304 {
305 #ifdef TARGET_WINDOWS
306 if (WSAGetLastError() != WSAEWOULDBLOCK)
307 #else
308 if (errno != EINPROGRESS)
309 #endif
310 return "unexpected connect fail";
311
312 { // wait for connect to complete
313 fd_set wset;
314 FD_ZERO(&wset);
315 FD_SET(soc, &wset);
316
317 result = select(FD_SETSIZE, 0, &wset, 0, &timeOut);
318 }
319
320 if (result < 0)
321 return "select fail";
322
323 if (result == 0) // timeout
324 return ""; // no error
325
326 { // verify socket connection state
327 int err_code = -1;
328 socklen_t code_len = sizeof (err_code);
329
330 result = getsockopt(soc, SOL_SOCKET, SO_ERROR, (char*) &err_code, &code_len);
331
332 if (result != 0)
333 return "getsockopt fail";
334
335 if (err_code != 0)
336 return ""; // no error, just not connected
337 }
338 }
339
340 if (tryRead)
341 {
342 fd_set rset;
343 FD_ZERO(&rset);
344 FD_SET(soc, &rset);
345
346 result = select(FD_SETSIZE, &rset, 0, 0, &timeOut);
347
348 if (result > 0)
349 {
350 char message [32];
351
352 result = recv(soc, message, sizeof(message), 0);
353 }
354
355 if (result == 0)
356 return ""; // no reply yet
357
358 if (result < 0)
359 return "recv fail";
360 }
361
362 return 0; // success
363 }
364
PingHost(unsigned long ipaddr,unsigned short port,unsigned int timeOutMs,bool readability_check)365 bool CNetworkBase::PingHost(unsigned long ipaddr, unsigned short port, unsigned int timeOutMs, bool readability_check)
366 {
367 if (port == 0) // use icmp ping
368 return PingHost (ipaddr, timeOutMs);
369
370 struct sockaddr_in addr;
371 addr.sin_family = AF_INET;
372 addr.sin_port = htons(port);
373 addr.sin_addr.s_addr = ipaddr;
374
375 SOCKET soc = socket(AF_INET, SOCK_STREAM, 0);
376
377 const char* err_msg = "invalid socket";
378
379 if (soc != INVALID_SOCKET)
380 {
381 struct timeval tmout;
382 tmout.tv_sec = timeOutMs / 1000;
383 tmout.tv_usec = (timeOutMs % 1000) * 1000;
384
385 err_msg = ConnectHostPort (soc, addr, tmout, readability_check);
386
387 (void) closesocket (soc);
388 }
389
390 if (err_msg && *err_msg)
391 {
392 #ifdef TARGET_WINDOWS
393 std::string sock_err = CWIN32Util::WUSysMsg(WSAGetLastError());
394 #else
395 std::string sock_err = strerror(errno);
396 #endif
397
398 CLog::Log(LOGERROR, "%s(%s:%d) - %s (%s)", __FUNCTION__, inet_ntoa(addr.sin_addr), port, err_msg, sock_err.c_str());
399 }
400
401 return err_msg == 0;
402 }
403
404 //creates, binds and listens tcp sockets on the desired port. Set bindLocal to
405 //true to bind to localhost only.
CreateTCPServerSocket(const int port,const bool bindLocal,const int backlog,const char * callerName)406 std::vector<SOCKET> CreateTCPServerSocket(const int port, const bool bindLocal, const int backlog, const char *callerName)
407 {
408 #ifdef WINSOCK_VERSION
409 int yes = 1;
410 #else
411 unsigned int yes = 1;
412 #endif
413
414 std::vector<SOCKET> sockets;
415 struct addrinfo* results = nullptr;
416
417 std::string sPort = StringUtils::Format("%d", port);
418 struct addrinfo hints = { 0 };
419 hints.ai_family = PF_UNSPEC;
420 hints.ai_socktype = SOCK_STREAM;
421 hints.ai_flags = AI_PASSIVE;
422 hints.ai_protocol = 0;
423
424 int error = getaddrinfo(bindLocal ? "localhost" : nullptr, sPort.c_str(), &hints, &results);
425 if (error != 0)
426 return sockets;
427
428 for (struct addrinfo* result = results; result != nullptr; result = result->ai_next)
429 {
430 SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
431 if (sock == INVALID_SOCKET)
432 continue;
433
434 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&yes), sizeof(yes));
435 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&yes), sizeof(yes));
436
437 if (bind(sock, result->ai_addr, result->ai_addrlen) != 0)
438 {
439 closesocket(sock);
440 CLog::Log(LOGDEBUG, "%s Server: Failed to bind %s serversocket", callerName, result->ai_family == AF_INET6 ? "IPv6" : "IPv4");
441 continue;
442 }
443
444 if (listen(sock, backlog) == 0)
445 sockets.push_back(sock);
446 else
447 {
448 closesocket(sock);
449 CLog::Log(LOGERROR, "%s Server: Failed to set listen", callerName);
450 }
451 }
452 freeaddrinfo(results);
453
454 if (sockets.empty())
455 CLog::Log(LOGERROR, "%s Server: Failed to create serversocket(s)", callerName);
456
457 return sockets;
458 }
459
WaitForNet()460 void CNetworkBase::WaitForNet()
461 {
462 const int timeout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_WAITFORNETWORK);
463 if (timeout <= 0)
464 return; // wait for network is disabled
465
466 // check if we have at least one network interface to wait for
467 if (!IsAvailable())
468 return;
469
470 CLog::Log(LOGINFO, "%s: Waiting for a network interface to come up (Timeout: %d s)", __FUNCTION__,
471 timeout);
472
473 const static int intervalMs = 200;
474 const int numMaxTries = (timeout * 1000) / intervalMs;
475
476 for(int i=0; i < numMaxTries; ++i)
477 {
478 if (i > 0)
479 KODI::TIME::Sleep(intervalMs);
480
481 if (IsConnected())
482 {
483 CLog::Log(LOGINFO, "%s: A network interface is up after waiting %d ms", __FUNCTION__,
484 i * intervalMs);
485 return;
486 }
487 }
488
489 CLog::Log(LOGINFO, "%s: No network interface did come up within %d s... Giving up...",
490 __FUNCTION__, timeout);
491 }
492
GetIpStr(const struct sockaddr * sa)493 std::string CNetworkBase::GetIpStr(const struct sockaddr* sa)
494 {
495 std::string result;
496 if (!sa)
497 return result;
498
499 char buffer[INET6_ADDRSTRLEN] = { 0 };
500 switch (sa->sa_family)
501 {
502 case AF_INET:
503 inet_ntop(AF_INET, &reinterpret_cast<const struct sockaddr_in *>(sa)->sin_addr, buffer, INET_ADDRSTRLEN);
504 break;
505 case AF_INET6:
506 inet_ntop(AF_INET6, &reinterpret_cast<const struct sockaddr_in6 *>(sa)->sin6_addr, buffer, INET6_ADDRSTRLEN);
507 break;
508 default:
509 return result;
510 }
511
512 result = buffer;
513 return result;
514 }
515
GetMaskByPrefixLength(uint8_t prefixLength)516 std::string CNetworkBase::GetMaskByPrefixLength(uint8_t prefixLength)
517 {
518 if (prefixLength > 32)
519 return "";
520
521 struct sockaddr_in sa;
522 sa.sin_family = AF_INET;
523 sa.sin_addr.s_addr = htonl(~((1 << (32u - prefixLength)) - 1));;
524 return CNetworkBase::GetIpStr(reinterpret_cast<struct sockaddr*>(&sa));
525 }
526