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"
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)
94 CApplicationMessenger::Get().NetworkMessage(SERVICES_UP, 0);
99 CApplicationMessenger::Get().NetworkMessage(SERVICES_DOWN, 0);
102 int CNetwork::ParseHex(char *str, unsigned char *addr)
111 if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1)
121 CStdString CNetwork::GetHostName(void)
124 if (gethostname(hostName, sizeof(hostName)))
125 return CStdString("unknown");
127 return CStdString(hostName);
130 CNetworkInterface* CNetwork::GetFirstConnectedInterface()
132 vector<CNetworkInterface*>& ifaces = GetInterfaceList();
133 vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
134 while (iter != ifaces.end())
136 CNetworkInterface* iface = *iter;
137 if (iface && iface->IsConnected())
145 bool CNetwork::HasInterfaceForIP(unsigned long address)
147 unsigned long subnet;
149 vector<CNetworkInterface*>& ifaces = GetInterfaceList();
150 vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
151 while (iter != ifaces.end())
153 CNetworkInterface* iface = *iter;
154 if (iface && iface->IsConnected())
156 subnet = ntohl(inet_addr(iface->GetCurrentNetmask()));
157 local = ntohl(inet_addr(iface->GetCurrentIPAddress()));
158 if( (address & subnet) == (local & subnet) )
167 bool CNetwork::IsAvailable(bool wait /*= false*/)
171 // NOTE: Not implemented in linuxport branch as 99.9% of the time
172 // we have the network setup already. Trunk code has a busy
173 // wait for 5 seconds here.
176 vector<CNetworkInterface*>& ifaces = GetInterfaceList();
177 return (ifaces.size() != 0);
180 bool CNetwork::IsConnected()
182 return GetFirstConnectedInterface() != NULL;
185 CNetworkInterface* CNetwork::GetInterfaceByName(CStdString& name)
187 vector<CNetworkInterface*>& ifaces = GetInterfaceList();
188 vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
189 while (iter != ifaces.end())
191 CNetworkInterface* iface = *iter;
192 if (iface && iface->GetName().Equals(name))
200 void CNetwork::NetworkMessage(EMESSAGE message, int param)
205 CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__);
206 CNetworkServices::Get().Start();
210 CLog::Log(LOGDEBUG, "%s - Signaling network services to stop",__FUNCTION__);
211 CNetworkServices::Get().Stop(false); // tell network services to stop, but don't wait for them yet
212 CLog::Log(LOGDEBUG, "%s - Waiting for network services to stop",__FUNCTION__);
213 CNetworkServices::Get().Stop(true); // wait for network services to stop
218 bool CNetwork::WakeOnLan(const char* mac)
221 unsigned char ethaddr[8];
222 unsigned char buf [128];
225 // Fetch the hardware address
226 if (!in_ether(mac, ethaddr))
228 CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac);
233 if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
235 CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno));
239 // Set socket options
240 struct sockaddr_in saddr;
241 saddr.sin_family = AF_INET;
242 saddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
243 saddr.sin_port = htons(9);
245 unsigned int value = 1;
246 if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
248 CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno));
253 // Build the magic packet (6 x 0xff + 16 x MAC address)
255 for (i = 0; i < 6; i++)
258 for (j = 0; j < 16; j++)
259 for (i = 0; i < 6; i++)
262 // Send the magic packet
263 if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
265 CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno));
271 CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac);
276 static const char* ConnectHostPort(SOCKET soc, const struct sockaddr_in& addr, struct timeval& timeOut, bool tryRead)
280 u_long nonblocking = 1;
281 int result = ioctlsocket(soc, FIONBIO, &nonblocking);
283 int result = fcntl(soc, F_SETFL, fcntl(soc, F_GETFL) | O_NONBLOCK);
287 return "set non-blocking option failed";
289 result = connect(soc, (struct sockaddr *)&addr, sizeof(addr)); // non-blocking connect, will fail ..
294 if (WSAGetLastError() != WSAEWOULDBLOCK)
296 if (errno != EINPROGRESS)
298 return "unexpected connect fail";
300 { // wait for connect to complete
305 result = select(FD_SETSIZE, 0, &wset, 0, &timeOut);
309 return "select fail";
311 if (result == 0) // timeout
312 return ""; // no error
314 { // verify socket connection state
316 socklen_t code_len = sizeof (err_code);
318 result = getsockopt(soc, SOL_SOCKET, SO_ERROR, (char*) &err_code, &code_len);
321 return "getsockopt fail";
324 return ""; // no error, just not connected
334 result = select(FD_SETSIZE, &rset, 0, 0, &timeOut);
340 result = recv(soc, message, sizeof(message), 0);
344 return ""; // no reply yet
353 bool CNetwork::PingHost(unsigned long ipaddr, unsigned short port, unsigned int timeOutMs, bool readability_check)
355 if (port == 0) // use icmp ping
356 return PingHost (ipaddr, timeOutMs);
358 struct sockaddr_in addr;
359 addr.sin_family = AF_INET;
360 addr.sin_port = htons(port);
361 addr.sin_addr.s_addr = ipaddr;
363 SOCKET soc = socket(AF_INET, SOCK_STREAM, 0);
365 const char* err_msg = "invalid socket";
367 if (soc != INVALID_SOCKET)
369 struct timeval tmout;
370 tmout.tv_sec = timeOutMs / 1000;
371 tmout.tv_usec = (timeOutMs % 1000) * 1000;
373 err_msg = ConnectHostPort (soc, addr, tmout, readability_check);
375 (void) closesocket (soc);
378 if (err_msg && *err_msg)
381 CStdString sock_err = WUSysMsg(WSAGetLastError());
383 CStdString sock_err = strerror(errno);
386 CLog::Log(LOGERROR, "%s(%s:%d) - %s (%s)", __FUNCTION__, inet_ntoa(addr.sin_addr), port, err_msg, sock_err.c_str());
392 //creates, binds and listens a tcp socket on the desired port. Set bindLocal to
393 //true to bind to localhost only. The socket will listen over ipv6 if possible
394 //and fall back to ipv4 if ipv6 is not available on the platform.
395 int CreateTCPServerSocket(const int port, const bool bindLocal, const int backlog, const char *callerName)
397 struct sockaddr_storage addr;
398 struct sockaddr_in6 *s6;
399 struct sockaddr_in *s4;
401 bool v4_fallback = false;
402 #ifdef TARGET_WINDOWS
403 // Windows XP and earlier don't support the IPV6_V6ONLY socket option
404 // so always fall back to IPv4 directly to keep old functionality
405 if (CSysInfo::GetWindowsVersion() <= CSysInfo::WindowsVersionWinXP)
409 #ifdef WINSOCK_VERSION
413 unsigned int yes = 1;
417 memset(&addr, 0, sizeof(addr));
420 (sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0)
422 // in case we're on ipv6, make sure the socket is dual stacked
423 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no)) < 0)
426 CStdString sock_err = WUSysMsg(WSAGetLastError());
428 CStdString sock_err = strerror(errno);
430 CLog::Log(LOGWARNING, "%s Server: Only IPv6 supported (%s)", callerName, sock_err.c_str());
436 sock = socket(PF_INET, SOCK_STREAM, 0);
439 if (sock == INVALID_SOCKET)
441 CLog::Log(LOGERROR, "%s Server: Failed to create serversocket", callerName);
442 return INVALID_SOCKET;
445 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
449 addr.ss_family = AF_INET;
450 s4 = (struct sockaddr_in *) &addr;
451 s4->sin_port = htons(port);
454 s4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
456 s4->sin_addr.s_addr = htonl(INADDR_ANY);
460 addr.ss_family = AF_INET6;
461 s6 = (struct sockaddr_in6 *) &addr;
462 s6->sin6_port = htons(port);
465 s6->sin6_addr = in6addr_loopback;
467 s6->sin6_addr = in6addr_any;
470 if (::bind( sock, (struct sockaddr *) &addr,
471 (addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) :
472 sizeof(struct sockaddr_in) ) < 0)
475 CLog::Log(LOGERROR, "%s Server: Failed to bind serversocket", callerName);
476 return INVALID_SOCKET;
479 if (listen(sock, backlog) < 0)
482 CLog::Log(LOGERROR, "%s Server: Failed to set listen", callerName);
483 return INVALID_SOCKET;