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