2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include <netinet/in.h>
22 #include <sys/socket.h>
23 #include <arpa/inet.h>
26 #include "ApplicationMessenger.h"
27 #include "network/NetworkServices.h"
28 #include "utils/log.h"
30 #include "utils/SystemInfo.h"
31 #include "win32/WIN32Util.h"
32 #include "utils/CharsetConverter.h"
35 /* slightly modified in_ether taken from the etherboot project (http://sourceforge.net/projects/etherboot) */
36 bool in_ether (const char *bufp, unsigned char *addr)
38 if (strlen(bufp) != 17)
43 unsigned char *ptr = addr;
49 while ((*bufp != '\0') && (i < 6))
56 else if (c >= 'a' && c <= 'f')
58 else if (c >= 'A' && c <= 'F')
67 else if (c >= 'a' && c <= 'f')
69 else if (c >= 'A' && c <= 'F')
71 else if (c == ':' || c == '-' || c == 0)
79 *ptr++ = (unsigned char) (val & 0377);
82 if (*bufp == ':' || *bufp == '-')
86 if (bufp - orig != 17)
92 int NetworkAccessPoint::getQuality() const
94 // Cisco dBm lookup table (partially nonlinear)
95 // Source: "Converting Signal Strength Percentage to dBm Values, 2002"
97 if (m_dBm >= -10) quality = 100;
98 else if (m_dBm >= -20) quality = 85 + (m_dBm + 20);
99 else if (m_dBm >= -30) quality = 77 + (m_dBm + 30);
100 else if (m_dBm >= -60) quality = 48 + (m_dBm + 60);
101 else if (m_dBm >= -98) quality = 13 + (m_dBm + 98);
102 else if (m_dBm >= -112) quality = 1 + (m_dBm + 112);
107 int NetworkAccessPoint::FreqToChannel(float frequency)
109 int IEEE80211Freq[] = {2412, 2417, 2422, 2427, 2432,
110 2437, 2442, 2447, 2452, 2457,
111 2462, 2467, 2472, 2484,
112 5180, 5200, 5210, 5220, 5240, 5250,
113 5260, 5280, 5290, 5300, 5320,
114 5745, 5760, 5765, 5785, 5800, 5805, 5825};
115 int IEEE80211Ch[] = { 1, 2, 3, 4, 5,
118 36, 40, 42, 44, 48, 50,
120 149, 152, 153, 157, 160, 161, 165};
121 // Round frequency to the nearest MHz
122 int mod_chan = (int)(frequency / 1000000 + 0.5f);
123 for (unsigned int i = 0; i < sizeof(IEEE80211Freq) / sizeof(int); ++i)
125 if (IEEE80211Freq[i] == mod_chan)
126 return IEEE80211Ch[i];
134 CApplicationMessenger::Get().NetworkMessage(SERVICES_UP, 0);
137 CNetwork::~CNetwork()
139 CApplicationMessenger::Get().NetworkMessage(SERVICES_DOWN, 0);
142 int CNetwork::ParseHex(char *str, unsigned char *addr)
151 if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1)
161 CStdString CNetwork::GetHostName(void)
164 if (gethostname(hostName, sizeof(hostName)))
165 return CStdString("unknown");
168 #ifdef TARGET_WINDOWS
169 g_charsetConverter.systemToUtf8(hostName, hostStr);
176 CNetworkInterface* CNetwork::GetFirstConnectedInterface()
178 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
179 std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
180 while (iter != ifaces.end())
182 CNetworkInterface* iface = *iter;
183 if (iface && iface->IsConnected())
191 bool CNetwork::HasInterfaceForIP(unsigned long address)
193 unsigned long subnet;
195 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
196 std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
197 while (iter != ifaces.end())
199 CNetworkInterface* iface = *iter;
200 if (iface && iface->IsConnected())
202 subnet = ntohl(inet_addr(iface->GetCurrentNetmask()));
203 local = ntohl(inet_addr(iface->GetCurrentIPAddress()));
204 if( (address & subnet) == (local & subnet) )
213 bool CNetwork::IsAvailable(bool wait /*= false*/)
217 // NOTE: Not implemented in linuxport branch as 99.9% of the time
218 // we have the network setup already. Trunk code has a busy
219 // wait for 5 seconds here.
222 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
223 return (ifaces.size() != 0);
226 bool CNetwork::IsConnected()
228 return GetFirstConnectedInterface() != NULL;
231 CNetworkInterface* CNetwork::GetInterfaceByName(CStdString& name)
233 std::vector<CNetworkInterface*>& ifaces = GetInterfaceList();
234 std::vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
235 while (iter != ifaces.end())
237 CNetworkInterface* iface = *iter;
238 if (iface && iface->GetName().Equals(name))
246 void CNetwork::NetworkMessage(EMESSAGE message, int param)
251 CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__);
252 CNetworkServices::Get().Start();
256 CLog::Log(LOGDEBUG, "%s - Signaling network services to stop",__FUNCTION__);
257 CNetworkServices::Get().Stop(false); // tell network services to stop, but don't wait for them yet
258 CLog::Log(LOGDEBUG, "%s - Waiting for network services to stop",__FUNCTION__);
259 CNetworkServices::Get().Stop(true); // wait for network services to stop
264 bool CNetwork::WakeOnLan(const char* mac)
267 unsigned char ethaddr[8];
268 unsigned char buf [128];
271 // Fetch the hardware address
272 if (!in_ether(mac, ethaddr))
274 CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac);
279 if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
281 CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno));
285 // Set socket options
286 struct sockaddr_in saddr;
287 saddr.sin_family = AF_INET;
288 saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
289 saddr.sin_port = htons(9);
291 unsigned int value = 1;
292 if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
294 CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno));
299 // Build the magic packet (6 x 0xff + 16 x MAC address)
301 for (i = 0; i < 6; i++)
304 for (j = 0; j < 16; j++)
305 for (i = 0; i < 6; i++)
308 // Send the magic packet
309 if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
311 CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno));
317 CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac);
322 static const char* ConnectHostPort(SOCKET soc, const struct sockaddr_in& addr, struct timeval& timeOut, bool tryRead)
325 #ifdef TARGET_WINDOWS
326 u_long nonblocking = 1;
327 int result = ioctlsocket(soc, FIONBIO, &nonblocking);
329 int result = fcntl(soc, F_SETFL, fcntl(soc, F_GETFL) | O_NONBLOCK);
333 return "set non-blocking option failed";
335 result = connect(soc, (struct sockaddr *)&addr, sizeof(addr)); // non-blocking connect, will fail ..
339 #ifdef TARGET_WINDOWS
340 if (WSAGetLastError() != WSAEWOULDBLOCK)
342 if (errno != EINPROGRESS)
344 return "unexpected connect fail";
346 { // wait for connect to complete
351 result = select(FD_SETSIZE, 0, &wset, 0, &timeOut);
355 return "select fail";
357 if (result == 0) // timeout
358 return ""; // no error
360 { // verify socket connection state
362 socklen_t code_len = sizeof (err_code);
364 result = getsockopt(soc, SOL_SOCKET, SO_ERROR, (char*) &err_code, &code_len);
367 return "getsockopt fail";
370 return ""; // no error, just not connected
380 result = select(FD_SETSIZE, &rset, 0, 0, &timeOut);
386 result = recv(soc, message, sizeof(message), 0);
390 return ""; // no reply yet
399 bool CNetwork::PingHost(unsigned long ipaddr, unsigned short port, unsigned int timeOutMs, bool readability_check)
401 if (port == 0) // use icmp ping
402 return PingHost (ipaddr, timeOutMs);
404 struct sockaddr_in addr;
405 addr.sin_family = AF_INET;
406 addr.sin_port = htons(port);
407 addr.sin_addr.s_addr = ipaddr;
409 SOCKET soc = socket(AF_INET, SOCK_STREAM, 0);
411 const char* err_msg = "invalid socket";
413 if (soc != INVALID_SOCKET)
415 struct timeval tmout;
416 tmout.tv_sec = timeOutMs / 1000;
417 tmout.tv_usec = (timeOutMs % 1000) * 1000;
419 err_msg = ConnectHostPort (soc, addr, tmout, readability_check);
421 (void) closesocket (soc);
424 if (err_msg && *err_msg)
426 #ifdef TARGET_WINDOWS
427 CStdString sock_err = CWIN32Util::WUSysMsg(WSAGetLastError());
429 CStdString sock_err = strerror(errno);
432 CLog::Log(LOGERROR, "%s(%s:%d) - %s (%s)", __FUNCTION__, inet_ntoa(addr.sin_addr), port, err_msg, sock_err.c_str());
438 //creates, binds and listens a tcp socket on the desired port. Set bindLocal to
439 //true to bind to localhost only. The socket will listen over ipv6 if possible
440 //and fall back to ipv4 if ipv6 is not available on the platform.
441 int CreateTCPServerSocket(const int port, const bool bindLocal, const int backlog, const char *callerName)
443 struct sockaddr_storage addr;
446 #ifdef WINSOCK_VERSION
450 unsigned int yes = 1;
455 if ((sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0)
457 // in case we're on ipv6, make sure the socket is dual stacked
458 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no)) < 0)
461 CStdString sock_err = CWIN32Util::WUSysMsg(WSAGetLastError());
463 CStdString sock_err = strerror(errno);
465 CLog::Log(LOGWARNING, "%s Server: Only IPv6 supported (%s)", callerName, sock_err.c_str());
468 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
470 struct sockaddr_in6 *s6;
471 memset(&addr, 0, sizeof(addr));
472 addr.ss_family = AF_INET6;
473 s6 = (struct sockaddr_in6 *) &addr;
474 s6->sin6_port = htons(port);
476 s6->sin6_addr = in6addr_loopback;
478 s6->sin6_addr = in6addr_any;
480 if (bind( sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in6)) < 0)
484 CLog::Log(LOGDEBUG, "%s Server: Failed to bind ipv6 serversocket, trying ipv4", callerName);
489 if (sock < 0 && (sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0)
491 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
493 struct sockaddr_in *s4;
494 memset(&addr, 0, sizeof(addr));
495 addr.ss_family = AF_INET;
496 s4 = (struct sockaddr_in *) &addr;
497 s4->sin_port = htons(port);
499 s4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
501 s4->sin_addr.s_addr = htonl(INADDR_ANY);
503 if (bind( sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0)
506 CLog::Log(LOGERROR, "%s Server: Failed to bind ipv4 serversocket", callerName);
507 return INVALID_SOCKET;
512 CLog::Log(LOGERROR, "%s Server: Failed to create serversocket", callerName);
513 return INVALID_SOCKET;
516 if (listen(sock, backlog) < 0)
519 CLog::Log(LOGERROR, "%s Server: Failed to set listen", callerName);
520 return INVALID_SOCKET;