2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
10 #include <netinet/in.h>
11 #include <sys/socket.h>
12 #include <arpa/inet.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"
22 #include "platform/win32/WIN32Util.h"
23 #include "utils/CharsetConverter.h"
25 #include "utils/StringUtils.h"
26 #include "utils/XTimeUtils.h"
28 /* slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) */
29 bool in_ether (const char *bufp
, unsigned char *addr
)
31 if (strlen(bufp
) != 17)
36 unsigned char *ptr
= addr
;
42 while ((*bufp
!= '\0') && (i
< 6))
49 else if (c
>= 'a' && c
<= 'f')
51 else if (c
>= 'A' && c
<= 'F')
60 else if (c
>= 'a' && c
<= 'f')
62 else if (c
>= 'A' && c
<= 'F')
64 else if (c
== ':' || c
== '-' || c
== 0)
72 *ptr
++ = (unsigned char) (val
& 0377);
75 if (*bufp
== ':' || *bufp
== '-')
79 if (bufp
- orig
!= 17)
85 CNetworkBase::CNetworkBase() :
86 m_services(new CNetworkServices())
88 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_NETWORKMESSAGE
, SERVICES_UP
, 0);
91 CNetworkBase::~CNetworkBase()
93 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_NETWORKMESSAGE
, SERVICES_DOWN
, 0);
96 int CNetworkBase::ParseHex(char *str
, unsigned char *addr
)
105 if (sscanf(str
, "%02x", (unsigned int *)&tmp
) != 1)
115 bool CNetworkBase::GetHostName(std::string
& hostname
)
118 if (gethostname(hostName
, sizeof(hostName
)))
121 #ifdef TARGET_WINDOWS
123 g_charsetConverter
.systemToUtf8(hostName
, hostStr
);
131 bool CNetworkBase::IsLocalHost(const std::string
& hostname
)
133 if (hostname
.empty())
136 if (StringUtils::StartsWith(hostname
, "127.")
137 || (hostname
== "::1")
138 || StringUtils::EqualsNoCase(hostname
, "localhost"))
141 std::string myhostname
;
142 if (GetHostName(myhostname
)
143 && StringUtils::EqualsNoCase(hostname
, myhostname
))
146 std::vector
<CNetworkInterface
*>& ifaces
= GetInterfaceList();
147 std::vector
<CNetworkInterface
*>::const_iterator iter
= ifaces
.begin();
148 while (iter
!= ifaces
.end())
150 CNetworkInterface
* iface
= *iter
;
151 if (iface
&& iface
->GetCurrentIPAddress() == hostname
)
160 CNetworkInterface
* CNetworkBase::GetFirstConnectedInterface()
162 CNetworkInterface
* fallbackInterface
= nullptr;
163 for (CNetworkInterface
* iface
: GetInterfaceList())
165 if (iface
&& iface
->IsConnected())
167 if (!iface
->GetCurrentDefaultGateway().empty())
169 else if (fallbackInterface
== nullptr)
170 fallbackInterface
= iface
;
174 return fallbackInterface
;
177 bool CNetworkBase::HasInterfaceForIP(unsigned long address
)
179 unsigned long subnet
;
181 std::vector
<CNetworkInterface
*>& ifaces
= GetInterfaceList();
182 std::vector
<CNetworkInterface
*>::const_iterator iter
= ifaces
.begin();
183 while (iter
!= ifaces
.end())
185 CNetworkInterface
* iface
= *iter
;
186 if (iface
&& iface
->IsConnected())
188 subnet
= ntohl(inet_addr(iface
->GetCurrentNetmask().c_str()));
189 local
= ntohl(inet_addr(iface
->GetCurrentIPAddress().c_str()));
190 if( (address
& subnet
) == (local
& subnet
) )
199 bool CNetworkBase::IsAvailable(void)
201 const std::vector
<CNetworkInterface
*>& ifaces
= GetInterfaceList();
202 return (ifaces
.size() != 0);
205 bool CNetworkBase::IsConnected()
207 return GetFirstConnectedInterface() != NULL
;
210 void CNetworkBase::NetworkMessage(EMESSAGE message
, int param
)
215 CLog::Log(LOGDEBUG
, "{} - Starting network services", __FUNCTION__
);
220 CLog::Log(LOGDEBUG
, "{} - Signaling network services to stop", __FUNCTION__
);
221 m_services
->Stop(false); // tell network services to stop, but don't wait for them yet
222 CLog::Log(LOGDEBUG
, "{} - Waiting for network services to stop", __FUNCTION__
);
223 m_services
->Stop(true); // wait for network services to stop
228 bool CNetworkBase::WakeOnLan(const char* mac
)
231 unsigned char ethaddr
[8];
232 unsigned char buf
[128];
235 // Fetch the hardware address
236 if (!in_ether(mac
, ethaddr
))
238 CLog::Log(LOGERROR
, "{} - Invalid hardware address specified ({})", __FUNCTION__
, mac
);
243 if ((packet
= socket (AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0)
245 CLog::Log(LOGERROR
, "{} - Unable to create socket ({})", __FUNCTION__
, strerror(errno
));
249 // Set socket options
250 struct sockaddr_in saddr
;
251 saddr
.sin_family
= AF_INET
;
252 saddr
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
253 saddr
.sin_port
= htons(9);
255 unsigned int value
= 1;
256 if (setsockopt (packet
, SOL_SOCKET
, SO_BROADCAST
, (char*) &value
, sizeof( unsigned int ) ) == SOCKET_ERROR
)
258 CLog::Log(LOGERROR
, "{} - Unable to set socket options ({})", __FUNCTION__
, strerror(errno
));
263 // Build the magic packet (6 x 0xff + 16 x MAC address)
265 for (i
= 0; i
< 6; i
++)
268 for (j
= 0; j
< 16; j
++)
269 for (i
= 0; i
< 6; i
++)
272 // Send the magic packet
273 if (sendto (packet
, (char *)buf
, 102, 0, (struct sockaddr
*)&saddr
, sizeof (saddr
)) < 0)
275 CLog::Log(LOGERROR
, "{} - Unable to send magic packet ({})", __FUNCTION__
, strerror(errno
));
281 CLog::Log(LOGINFO
, "{} - Magic packet send to '{}'", __FUNCTION__
, mac
);
286 static const char* ConnectHostPort(SOCKET soc
, const struct sockaddr_in
& addr
, struct timeval
& timeOut
, bool tryRead
)
289 #ifdef TARGET_WINDOWS
290 u_long nonblocking
= 1;
291 int result
= ioctlsocket(soc
, FIONBIO
, &nonblocking
);
293 int result
= fcntl(soc
, F_SETFL
, fcntl(soc
, F_GETFL
) | O_NONBLOCK
);
297 return "set non-blocking option failed";
299 result
= connect(soc
, (const struct sockaddr
*)&addr
, sizeof(addr
)); // non-blocking connect, will fail ..
303 #ifdef TARGET_WINDOWS
304 if (WSAGetLastError() != WSAEWOULDBLOCK
)
306 if (errno
!= EINPROGRESS
)
308 return "unexpected connect fail";
310 { // wait for connect to complete
315 result
= select(FD_SETSIZE
, 0, &wset
, 0, &timeOut
);
319 return "select fail";
321 if (result
== 0) // timeout
322 return ""; // no error
324 { // verify socket connection state
326 socklen_t code_len
= sizeof (err_code
);
328 result
= getsockopt(soc
, SOL_SOCKET
, SO_ERROR
, (char*) &err_code
, &code_len
);
331 return "getsockopt fail";
334 return ""; // no error, just not connected
344 result
= select(FD_SETSIZE
, &rset
, 0, 0, &timeOut
);
350 result
= recv(soc
, message
, sizeof(message
), 0);
354 return ""; // no reply yet
363 bool CNetworkBase::PingHost(unsigned long ipaddr
, unsigned short port
, unsigned int timeOutMs
, bool readability_check
)
365 if (port
== 0) // use icmp ping
366 return PingHost (ipaddr
, timeOutMs
);
368 struct sockaddr_in addr
;
369 addr
.sin_family
= AF_INET
;
370 addr
.sin_port
= htons(port
);
371 addr
.sin_addr
.s_addr
= ipaddr
;
373 SOCKET soc
= socket(AF_INET
, SOCK_STREAM
, 0);
375 const char* err_msg
= "invalid socket";
377 if (soc
!= INVALID_SOCKET
)
379 struct timeval tmout
;
380 tmout
.tv_sec
= timeOutMs
/ 1000;
381 tmout
.tv_usec
= (timeOutMs
% 1000) * 1000;
383 err_msg
= ConnectHostPort (soc
, addr
, tmout
, readability_check
);
385 (void) closesocket (soc
);
388 if (err_msg
&& *err_msg
)
390 #ifdef TARGET_WINDOWS
391 std::string sock_err
= CWIN32Util::WUSysMsg(WSAGetLastError());
393 std::string sock_err
= strerror(errno
);
396 CLog::Log(LOGERROR
, "{}({}:{}) - {} ({})", __FUNCTION__
, inet_ntoa(addr
.sin_addr
), port
,
403 //creates, binds and listens tcp sockets on the desired port. Set bindLocal to
404 //true to bind to localhost only.
405 std::vector
<SOCKET
> CreateTCPServerSocket(const int port
, const bool bindLocal
, const int backlog
, const char *callerName
)
407 #ifdef WINSOCK_VERSION
410 unsigned int yes
= 1;
413 std::vector
<SOCKET
> sockets
;
414 struct addrinfo
* results
= nullptr;
416 std::string sPort
= std::to_string(port
);
417 struct addrinfo hints
= {};
418 hints
.ai_family
= PF_UNSPEC
;
419 hints
.ai_socktype
= SOCK_STREAM
;
420 hints
.ai_flags
= AI_PASSIVE
;
421 hints
.ai_protocol
= 0;
423 int error
= getaddrinfo(bindLocal
? "localhost" : nullptr, sPort
.c_str(), &hints
, &results
);
427 for (struct addrinfo
* result
= results
; result
!= nullptr; result
= result
->ai_next
)
429 SOCKET sock
= socket(result
->ai_family
, result
->ai_socktype
, result
->ai_protocol
);
430 if (sock
== INVALID_SOCKET
)
433 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, reinterpret_cast<const char*>(&yes
), sizeof(yes
));
434 setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, reinterpret_cast<const char*>(&yes
), sizeof(yes
));
436 if (bind(sock
, result
->ai_addr
, result
->ai_addrlen
) != 0)
439 CLog::Log(LOGDEBUG
, "{} Server: Failed to bind {} serversocket", callerName
,
440 result
->ai_family
== AF_INET6
? "IPv6" : "IPv4");
444 if (listen(sock
, backlog
) == 0)
445 sockets
.push_back(sock
);
449 CLog::Log(LOGERROR
, "{} Server: Failed to set listen", callerName
);
452 freeaddrinfo(results
);
455 CLog::Log(LOGERROR
, "{} Server: Failed to create serversocket(s)", callerName
);
460 void CNetworkBase::WaitForNet()
462 const int timeout
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_WAITFORNETWORK
);
464 return; // wait for network is disabled
466 // check if we have at least one network interface to wait for
470 CLog::Log(LOGINFO
, "{}: Waiting for a network interface to come up (Timeout: {} s)", __FUNCTION__
,
473 const static int intervalMs
= 200;
474 const int numMaxTries
= (timeout
* 1000) / intervalMs
;
476 for(int i
=0; i
< numMaxTries
; ++i
)
479 KODI::TIME::Sleep(std::chrono::milliseconds(intervalMs
));
483 CLog::Log(LOGINFO
, "{}: A network interface is up after waiting {} ms", __FUNCTION__
,
489 CLog::Log(LOGINFO
, "{}: No network interface did come up within {} s... Giving up...",
490 __FUNCTION__
, timeout
);
493 std::string
CNetworkBase::GetIpStr(const struct sockaddr
* sa
)
499 char buffer
[INET6_ADDRSTRLEN
] = {};
500 switch (sa
->sa_family
)
503 inet_ntop(AF_INET
, &reinterpret_cast<const struct sockaddr_in
*>(sa
)->sin_addr
, buffer
, INET_ADDRSTRLEN
);
506 inet_ntop(AF_INET6
, &reinterpret_cast<const struct sockaddr_in6
*>(sa
)->sin6_addr
, buffer
, INET6_ADDRSTRLEN
);
516 std::string
CNetworkBase::GetMaskByPrefixLength(uint8_t prefixLength
)
518 if (prefixLength
> 32)
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
));