summaryrefslogtreecommitdiffstats
path: root/xbmc/network/Network.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/network/Network.cpp')
-rw-r--r--xbmc/network/Network.cpp525
1 files changed, 525 insertions, 0 deletions
diff --git a/xbmc/network/Network.cpp b/xbmc/network/Network.cpp
new file mode 100644
index 0000000..69a1513
--- /dev/null
+++ b/xbmc/network/Network.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "Network.h"
+#include "ServiceBroker.h"
+#include "messaging/ApplicationMessenger.h"
+#include "network/NetworkServices.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/log.h"
+#ifdef TARGET_WINDOWS
+#include "platform/win32/WIN32Util.h"
+#include "utils/CharsetConverter.h"
+#endif
+#include "utils/StringUtils.h"
+#include "utils/XTimeUtils.h"
+
+/* slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) */
+bool in_ether (const char *bufp, unsigned char *addr)
+{
+ if (strlen(bufp) != 17)
+ return false;
+
+ char c;
+ const char *orig;
+ unsigned char *ptr = addr;
+ unsigned val;
+
+ int i = 0;
+ orig = bufp;
+
+ while ((*bufp != '\0') && (i < 6))
+ {
+ val = 0;
+ c = *bufp++;
+
+ if (isdigit(c))
+ val = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val = c - 'A' + 10;
+ else
+ return false;
+
+ val <<= 4;
+ c = *bufp;
+ if (isdigit(c))
+ val |= c - '0';
+ else if (c >= 'a' && c <= 'f')
+ val |= c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ val |= c - 'A' + 10;
+ else if (c == ':' || c == '-' || c == 0)
+ val >>= 4;
+ else
+ return false;
+
+ if (c != 0)
+ bufp++;
+
+ *ptr++ = (unsigned char) (val & 0377);
+ i++;
+
+ if (*bufp == ':' || *bufp == '-')
+ bufp++;
+ }
+
+ if (bufp - orig != 17)
+ return false;
+
+ return true;
+}
+
+CNetworkBase::CNetworkBase() :
+ m_services(new CNetworkServices())
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_NETWORKMESSAGE, SERVICES_UP, 0);
+}
+
+CNetworkBase::~CNetworkBase()
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_NETWORKMESSAGE, SERVICES_DOWN, 0);
+}
+
+int CNetworkBase::ParseHex(char *str, unsigned char *addr)
+{
+ int len = 0;
+
+ while (*str)
+ {
+ int tmp;
+ if (str[1] == 0)
+ return -1;
+ if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1)
+ return -1;
+ addr[len] = tmp;
+ len++;
+ str += 2;
+ }
+
+ return len;
+}
+
+bool CNetworkBase::GetHostName(std::string& hostname)
+{
+ char hostName[128];
+ if (gethostname(hostName, sizeof(hostName)))
+ return false;
+
+#ifdef TARGET_WINDOWS
+ std::string hostStr;
+ g_charsetConverter.systemToUtf8(hostName, hostStr);
+ hostname = hostStr;
+#else
+ hostname = hostName;
+#endif
+ return true;
+}
+
+bool CNetworkBase::IsLocalHost(const std::string& hostname)
+{
+ if (hostname.empty())
+ return false;
+
+ if (StringUtils::StartsWith(hostname, "127.")
+ || (hostname == "::1")
+ || StringUtils::EqualsNoCase(hostname, "localhost"))
+ return true;
+
+ std::string myhostname;
+ if (GetHostName(myhostname)
+ && StringUtils::EqualsNoCase(hostname, myhostname))
+ return true;
+
+ std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
+ std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
+ while (iter != ifaces.end())
+ {
+ CNetworkInterface* iface = *iter;
+ if (iface && iface->GetCurrentIPAddress() == hostname)
+ return true;
+
+ ++iter;
+ }
+
+ return false;
+}
+
+CNetworkInterface* CNetworkBase::GetFirstConnectedInterface()
+{
+ CNetworkInterface* fallbackInterface = nullptr;
+ for (CNetworkInterface* iface : GetInterfaceList())
+ {
+ if (iface && iface->IsConnected())
+ {
+ if (!iface->GetCurrentDefaultGateway().empty())
+ return iface;
+ else if (fallbackInterface == nullptr)
+ fallbackInterface = iface;
+ }
+ }
+
+ return fallbackInterface;
+}
+
+bool CNetworkBase::HasInterfaceForIP(unsigned long address)
+{
+ unsigned long subnet;
+ unsigned long local;
+ std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
+ std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
+ while (iter != ifaces.end())
+ {
+ CNetworkInterface* iface = *iter;
+ if (iface && iface->IsConnected())
+ {
+ subnet = ntohl(inet_addr(iface->GetCurrentNetmask().c_str()));
+ local = ntohl(inet_addr(iface->GetCurrentIPAddress().c_str()));
+ if( (address & subnet) == (local & subnet) )
+ return true;
+ }
+ ++iter;
+ }
+
+ return false;
+}
+
+bool CNetworkBase::IsAvailable(void)
+{
+ const std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
+ return (ifaces.size() != 0);
+}
+
+bool CNetworkBase::IsConnected()
+{
+ return GetFirstConnectedInterface() != NULL;
+}
+
+void CNetworkBase::NetworkMessage(EMESSAGE message, int param)
+{
+ switch( message )
+ {
+ case SERVICES_UP:
+ CLog::Log(LOGDEBUG, "{} - Starting network services", __FUNCTION__);
+ m_services->Start();
+ break;
+
+ case SERVICES_DOWN:
+ CLog::Log(LOGDEBUG, "{} - Signaling network services to stop", __FUNCTION__);
+ m_services->Stop(false); // tell network services to stop, but don't wait for them yet
+ CLog::Log(LOGDEBUG, "{} - Waiting for network services to stop", __FUNCTION__);
+ m_services->Stop(true); // wait for network services to stop
+ break;
+ }
+}
+
+bool CNetworkBase::WakeOnLan(const char* mac)
+{
+ int i, j, packet;
+ unsigned char ethaddr[8];
+ unsigned char buf [128];
+ unsigned char *ptr;
+
+ // Fetch the hardware address
+ if (!in_ether(mac, ethaddr))
+ {
+ CLog::Log(LOGERROR, "{} - Invalid hardware address specified ({})", __FUNCTION__, mac);
+ return false;
+ }
+
+ // Setup the socket
+ if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ {
+ CLog::Log(LOGERROR, "{} - Unable to create socket ({})", __FUNCTION__, strerror(errno));
+ return false;
+ }
+
+ // Set socket options
+ struct sockaddr_in saddr;
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ saddr.sin_port = htons(9);
+
+ unsigned int value = 1;
+ if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
+ {
+ CLog::Log(LOGERROR, "{} - Unable to set socket options ({})", __FUNCTION__, strerror(errno));
+ closesocket(packet);
+ return false;
+ }
+
+ // Build the magic packet (6 x 0xff + 16 x MAC address)
+ ptr = buf;
+ for (i = 0; i < 6; i++)
+ *ptr++ = 0xff;
+
+ for (j = 0; j < 16; j++)
+ for (i = 0; i < 6; i++)
+ *ptr++ = ethaddr[i];
+
+ // Send the magic packet
+ if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
+ {
+ CLog::Log(LOGERROR, "{} - Unable to send magic packet ({})", __FUNCTION__, strerror(errno));
+ closesocket(packet);
+ return false;
+ }
+
+ closesocket(packet);
+ CLog::Log(LOGINFO, "{} - Magic packet send to '{}'", __FUNCTION__, mac);
+ return true;
+}
+
+// ping helper
+static const char* ConnectHostPort(SOCKET soc, const struct sockaddr_in& addr, struct timeval& timeOut, bool tryRead)
+{
+ // set non-blocking
+#ifdef TARGET_WINDOWS
+ u_long nonblocking = 1;
+ int result = ioctlsocket(soc, FIONBIO, &nonblocking);
+#else
+ int result = fcntl(soc, F_SETFL, fcntl(soc, F_GETFL) | O_NONBLOCK);
+#endif
+
+ if (result != 0)
+ return "set non-blocking option failed";
+
+ result = connect(soc, (const struct sockaddr *)&addr, sizeof(addr)); // non-blocking connect, will fail ..
+
+ if (result < 0)
+ {
+#ifdef TARGET_WINDOWS
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+#else
+ if (errno != EINPROGRESS)
+#endif
+ return "unexpected connect fail";
+
+ { // wait for connect to complete
+ fd_set wset;
+ FD_ZERO(&wset);
+ FD_SET(soc, &wset);
+
+ result = select(FD_SETSIZE, 0, &wset, 0, &timeOut);
+ }
+
+ if (result < 0)
+ return "select fail";
+
+ if (result == 0) // timeout
+ return ""; // no error
+
+ { // verify socket connection state
+ int err_code = -1;
+ socklen_t code_len = sizeof (err_code);
+
+ result = getsockopt(soc, SOL_SOCKET, SO_ERROR, (char*) &err_code, &code_len);
+
+ if (result != 0)
+ return "getsockopt fail";
+
+ if (err_code != 0)
+ return ""; // no error, just not connected
+ }
+ }
+
+ if (tryRead)
+ {
+ fd_set rset;
+ FD_ZERO(&rset);
+ FD_SET(soc, &rset);
+
+ result = select(FD_SETSIZE, &rset, 0, 0, &timeOut);
+
+ if (result > 0)
+ {
+ char message [32];
+
+ result = recv(soc, message, sizeof(message), 0);
+ }
+
+ if (result == 0)
+ return ""; // no reply yet
+
+ if (result < 0)
+ return "recv fail";
+ }
+
+ return 0; // success
+}
+
+bool CNetworkBase::PingHost(unsigned long ipaddr, unsigned short port, unsigned int timeOutMs, bool readability_check)
+{
+ if (port == 0) // use icmp ping
+ return PingHost (ipaddr, timeOutMs);
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = ipaddr;
+
+ SOCKET soc = socket(AF_INET, SOCK_STREAM, 0);
+
+ const char* err_msg = "invalid socket";
+
+ if (soc != INVALID_SOCKET)
+ {
+ struct timeval tmout;
+ tmout.tv_sec = timeOutMs / 1000;
+ tmout.tv_usec = (timeOutMs % 1000) * 1000;
+
+ err_msg = ConnectHostPort (soc, addr, tmout, readability_check);
+
+ (void) closesocket (soc);
+ }
+
+ if (err_msg && *err_msg)
+ {
+#ifdef TARGET_WINDOWS
+ std::string sock_err = CWIN32Util::WUSysMsg(WSAGetLastError());
+#else
+ std::string sock_err = strerror(errno);
+#endif
+
+ CLog::Log(LOGERROR, "{}({}:{}) - {} ({})", __FUNCTION__, inet_ntoa(addr.sin_addr), port,
+ err_msg, sock_err);
+ }
+
+ return err_msg == 0;
+}
+
+//creates, binds and listens tcp sockets on the desired port. Set bindLocal to
+//true to bind to localhost only.
+std::vector<SOCKET> CreateTCPServerSocket(const int port, const bool bindLocal, const int backlog, const char *callerName)
+{
+#ifdef WINSOCK_VERSION
+ int yes = 1;
+#else
+ unsigned int yes = 1;
+#endif
+
+ std::vector<SOCKET> sockets;
+ struct addrinfo* results = nullptr;
+
+ std::string sPort = std::to_string(port);
+ struct addrinfo hints = {};
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_protocol = 0;
+
+ int error = getaddrinfo(bindLocal ? "localhost" : nullptr, sPort.c_str(), &hints, &results);
+ if (error != 0)
+ return sockets;
+
+ for (struct addrinfo* result = results; result != nullptr; result = result->ai_next)
+ {
+ SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
+ if (sock == INVALID_SOCKET)
+ continue;
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&yes), sizeof(yes));
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&yes), sizeof(yes));
+
+ if (bind(sock, result->ai_addr, result->ai_addrlen) != 0)
+ {
+ closesocket(sock);
+ CLog::Log(LOGDEBUG, "{} Server: Failed to bind {} serversocket", callerName,
+ result->ai_family == AF_INET6 ? "IPv6" : "IPv4");
+ continue;
+ }
+
+ if (listen(sock, backlog) == 0)
+ sockets.push_back(sock);
+ else
+ {
+ closesocket(sock);
+ CLog::Log(LOGERROR, "{} Server: Failed to set listen", callerName);
+ }
+ }
+ freeaddrinfo(results);
+
+ if (sockets.empty())
+ CLog::Log(LOGERROR, "{} Server: Failed to create serversocket(s)", callerName);
+
+ return sockets;
+}
+
+void CNetworkBase::WaitForNet()
+{
+ const int timeout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_WAITFORNETWORK);
+ if (timeout <= 0)
+ return; // wait for network is disabled
+
+ // check if we have at least one network interface to wait for
+ if (!IsAvailable())
+ return;
+
+ CLog::Log(LOGINFO, "{}: Waiting for a network interface to come up (Timeout: {} s)", __FUNCTION__,
+ timeout);
+
+ const static int intervalMs = 200;
+ const int numMaxTries = (timeout * 1000) / intervalMs;
+
+ for(int i=0; i < numMaxTries; ++i)
+ {
+ if (i > 0)
+ KODI::TIME::Sleep(std::chrono::milliseconds(intervalMs));
+
+ if (IsConnected())
+ {
+ CLog::Log(LOGINFO, "{}: A network interface is up after waiting {} ms", __FUNCTION__,
+ i * intervalMs);
+ return;
+ }
+ }
+
+ CLog::Log(LOGINFO, "{}: No network interface did come up within {} s... Giving up...",
+ __FUNCTION__, timeout);
+}
+
+std::string CNetworkBase::GetIpStr(const struct sockaddr* sa)
+{
+ std::string result;
+ if (!sa)
+ return result;
+
+ char buffer[INET6_ADDRSTRLEN] = {};
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ inet_ntop(AF_INET, &reinterpret_cast<const struct sockaddr_in *>(sa)->sin_addr, buffer, INET_ADDRSTRLEN);
+ break;
+ case AF_INET6:
+ inet_ntop(AF_INET6, &reinterpret_cast<const struct sockaddr_in6 *>(sa)->sin6_addr, buffer, INET6_ADDRSTRLEN);
+ break;
+ default:
+ return result;
+ }
+
+ result = buffer;
+ return result;
+}
+
+std::string CNetworkBase::GetMaskByPrefixLength(uint8_t prefixLength)
+{
+ if (prefixLength > 32)
+ return "";
+
+ struct sockaddr_in sa;
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(~((1 << (32u - prefixLength)) - 1));;
+ return CNetworkBase::GetIpStr(reinterpret_cast<struct sockaddr*>(&sa));
+}