[win32] always use IPv4 sockets on WinXP (because it doesn't support dual-stack IPv4...
[vuplus_xbmc] / xbmc / network / Network.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include <netinet/in.h>
22 #include <sys/socket.h>
23 #include <arpa/inet.h>
24
25 #include "Network.h"
26 #include "ApplicationMessenger.h"
27 #include "network/NetworkServices.h"
28 #include "utils/log.h"
29 #ifdef TARGET_WINDOWS
30 #include "utils/SystemInfo.h"
31 #endif
32
33 using namespace std;
34
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)
37 {
38   if (strlen(bufp) != 17)
39     return false;
40
41   char c;
42   const char *orig;
43   unsigned char *ptr = addr;
44   unsigned val;
45
46   int i = 0;
47   orig = bufp;
48
49   while ((*bufp != '\0') && (i < 6))
50   {
51     val = 0;
52     c = *bufp++;
53
54     if (isdigit(c))
55       val = c - '0';
56     else if (c >= 'a' && c <= 'f')
57       val = c - 'a' + 10;
58     else if (c >= 'A' && c <= 'F')
59       val = c - 'A' + 10;
60     else
61       return false;
62
63     val <<= 4;
64     c = *bufp;
65     if (isdigit(c))
66       val |= c - '0';
67     else if (c >= 'a' && c <= 'f')
68       val |= c - 'a' + 10;
69     else if (c >= 'A' && c <= 'F')
70       val |= c - 'A' + 10;
71     else if (c == ':' || c == '-' || c == 0)
72       val >>= 4;
73     else
74       return false;
75
76     if (c != 0)
77       bufp++;
78
79     *ptr++ = (unsigned char) (val & 0377);
80     i++;
81
82     if (*bufp == ':' || *bufp == '-')
83       bufp++;
84   }
85
86   if (bufp - orig != 17)
87     return false;
88
89   return true;
90 }
91
92 CNetwork::CNetwork()
93 {
94   CApplicationMessenger::Get().NetworkMessage(SERVICES_UP, 0);
95 }
96
97 CNetwork::~CNetwork()
98 {
99   CApplicationMessenger::Get().NetworkMessage(SERVICES_DOWN, 0);
100 }
101
102 int CNetwork::ParseHex(char *str, unsigned char *addr)
103 {
104    int len = 0;
105
106    while (*str)
107    {
108       int tmp;
109       if (str[1] == 0)
110          return -1;
111       if (sscanf(str, "%02x", (unsigned int *)&tmp) != 1)
112          return -1;
113       addr[len] = tmp;
114       len++;
115       str += 2;
116    }
117
118    return len;
119 }
120
121 CStdString CNetwork::GetHostName(void)
122 {
123   char hostName[128];
124   if (gethostname(hostName, sizeof(hostName)))
125     return CStdString("unknown");
126   else
127     return CStdString(hostName);
128 }
129
130 CNetworkInterface* CNetwork::GetFirstConnectedInterface()
131 {
132    vector<CNetworkInterface*>& ifaces = GetInterfaceList();
133    vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
134    while (iter != ifaces.end())
135    {
136       CNetworkInterface* iface = *iter;
137       if (iface && iface->IsConnected())
138          return iface;
139       ++iter;
140    }
141
142    return NULL;
143 }
144
145 bool CNetwork::HasInterfaceForIP(unsigned long address)
146 {
147    unsigned long subnet;
148    unsigned long local;
149    vector<CNetworkInterface*>& ifaces = GetInterfaceList();
150    vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
151    while (iter != ifaces.end())
152    {
153       CNetworkInterface* iface = *iter;
154       if (iface && iface->IsConnected())
155       {
156          subnet = ntohl(inet_addr(iface->GetCurrentNetmask()));
157          local = ntohl(inet_addr(iface->GetCurrentIPAddress()));
158          if( (address & subnet) == (local & subnet) )
159             return true;
160       }
161       ++iter;
162    }
163
164    return false;
165 }
166
167 bool CNetwork::IsAvailable(bool wait /*= false*/)
168 {
169   if (wait)
170   {
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.
174   }
175
176   vector<CNetworkInterface*>& ifaces = GetInterfaceList();
177   return (ifaces.size() != 0);
178 }
179
180 bool CNetwork::IsConnected()
181 {
182    return GetFirstConnectedInterface() != NULL;
183 }
184
185 CNetworkInterface* CNetwork::GetInterfaceByName(CStdString& name)
186 {
187    vector<CNetworkInterface*>& ifaces = GetInterfaceList();
188    vector<CNetworkInterface*>::const_iterator iter = ifaces.begin();
189    while (iter != ifaces.end())
190    {
191       CNetworkInterface* iface = *iter;
192       if (iface && iface->GetName().Equals(name))
193          return iface;
194       ++iter;
195    }
196
197    return NULL;
198 }
199
200 void CNetwork::NetworkMessage(EMESSAGE message, int param)
201 {
202   switch( message )
203   {
204     case SERVICES_UP:
205       CLog::Log(LOGDEBUG, "%s - Starting network services",__FUNCTION__);
206       CNetworkServices::Get().Start();
207       break;
208
209     case SERVICES_DOWN:
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
214       break;
215   }
216 }
217
218 bool CNetwork::WakeOnLan(const char* mac)
219 {
220   int i, j, packet;
221   unsigned char ethaddr[8];
222   unsigned char buf [128];
223   unsigned char *ptr;
224
225   // Fetch the hardware address
226   if (!in_ether(mac, ethaddr))
227   {
228     CLog::Log(LOGERROR, "%s - Invalid hardware address specified (%s)", __FUNCTION__, mac);
229     return false;
230   }
231
232   // Setup the socket
233   if ((packet = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
234   {
235     CLog::Log(LOGERROR, "%s - Unable to create socket (%s)", __FUNCTION__, strerror (errno));
236     return false;
237   }
238  
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);
244
245   unsigned int value = 1;
246   if (setsockopt (packet, SOL_SOCKET, SO_BROADCAST, (char*) &value, sizeof( unsigned int ) ) == SOCKET_ERROR)
247   {
248     CLog::Log(LOGERROR, "%s - Unable to set socket options (%s)", __FUNCTION__, strerror (errno));
249     closesocket(packet);
250     return false;
251   }
252  
253   // Build the magic packet (6 x 0xff + 16 x MAC address)
254   ptr = buf;
255   for (i = 0; i < 6; i++)
256     *ptr++ = 0xff;
257
258   for (j = 0; j < 16; j++)
259     for (i = 0; i < 6; i++)
260       *ptr++ = ethaddr[i];
261  
262   // Send the magic packet
263   if (sendto (packet, (char *)buf, 102, 0, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
264   {
265     CLog::Log(LOGERROR, "%s - Unable to send magic packet (%s)", __FUNCTION__, strerror (errno));
266     closesocket(packet);
267     return false;
268   }
269
270   closesocket(packet);
271   CLog::Log(LOGINFO, "%s - Magic packet send to '%s'", __FUNCTION__, mac);
272   return true;
273 }
274
275 // ping helper
276 static const char* ConnectHostPort(SOCKET soc, const struct sockaddr_in& addr, struct timeval& timeOut, bool tryRead)
277 {
278   // set non-blocking
279 #ifdef _MSC_VER
280   u_long nonblocking = 1;
281   int result = ioctlsocket(soc, FIONBIO, &nonblocking);
282 #else
283   int result = fcntl(soc, F_SETFL, fcntl(soc, F_GETFL) | O_NONBLOCK);
284 #endif
285
286   if (result != 0)
287     return "set non-blocking option failed";
288
289   result = connect(soc, (struct sockaddr *)&addr, sizeof(addr)); // non-blocking connect, will fail ..
290
291   if (result < 0)
292   {
293 #ifdef _MSC_VER
294     if (WSAGetLastError() != WSAEWOULDBLOCK)
295 #else
296     if (errno != EINPROGRESS)
297 #endif
298       return "unexpected connect fail";
299
300     { // wait for connect to complete
301       fd_set wset;
302       FD_ZERO(&wset); 
303       FD_SET(soc, &wset); 
304
305       result = select(FD_SETSIZE, 0, &wset, 0, &timeOut);
306     }
307
308     if (result < 0)
309       return "select fail";
310
311     if (result == 0) // timeout
312       return ""; // no error
313
314     { // verify socket connection state
315       int err_code = -1;
316       socklen_t code_len = sizeof (err_code);
317
318       result = getsockopt(soc, SOL_SOCKET, SO_ERROR, (char*) &err_code, &code_len);
319
320       if (result != 0)
321         return "getsockopt fail";
322
323       if (err_code != 0)
324         return ""; // no error, just not connected
325     }
326   }
327
328   if (tryRead)
329   {
330     fd_set rset;
331     FD_ZERO(&rset); 
332     FD_SET(soc, &rset); 
333
334     result = select(FD_SETSIZE, &rset, 0, 0, &timeOut);
335
336     if (result > 0)
337     {
338       char message [32];
339
340       result = recv(soc, message, sizeof(message), 0);
341     }
342
343     if (result == 0)
344       return ""; // no reply yet
345
346     if (result < 0)
347       return "recv fail";
348   }
349
350   return 0; // success
351 }
352
353 bool CNetwork::PingHost(unsigned long ipaddr, unsigned short port, unsigned int timeOutMs, bool readability_check)
354 {
355   if (port == 0) // use icmp ping
356     return PingHost (ipaddr, timeOutMs);
357
358   struct sockaddr_in addr; 
359   addr.sin_family = AF_INET; 
360   addr.sin_port = htons(port); 
361   addr.sin_addr.s_addr = ipaddr; 
362
363   SOCKET soc = socket(AF_INET, SOCK_STREAM, 0); 
364
365   const char* err_msg = "invalid socket";
366
367   if (soc != INVALID_SOCKET)
368   {
369     struct timeval tmout; 
370     tmout.tv_sec = timeOutMs / 1000; 
371     tmout.tv_usec = (timeOutMs % 1000) * 1000; 
372
373     err_msg = ConnectHostPort (soc, addr, tmout, readability_check);
374
375     (void) closesocket (soc);
376   }
377
378   if (err_msg && *err_msg)
379   {
380 #ifdef _MSC_VER
381     CStdString sock_err = WUSysMsg(WSAGetLastError());
382 #else
383     CStdString sock_err = strerror(errno);
384 #endif
385
386     CLog::Log(LOGERROR, "%s(%s:%d) - %s (%s)", __FUNCTION__, inet_ntoa(addr.sin_addr), port, err_msg, sock_err.c_str());
387   }
388
389   return err_msg == 0;
390 }
391
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)
396 {
397   struct sockaddr_storage addr;
398   struct sockaddr_in6 *s6;
399   struct sockaddr_in  *s4;
400   int    sock;
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)
406     v4_fallback = true;
407 #endif
408
409 #ifdef WINSOCK_VERSION
410   int yes = 1;
411   int no = 0;
412 #else
413   unsigned int yes = 1;
414   unsigned int no = 0;
415 #endif
416   
417   memset(&addr, 0, sizeof(addr));
418   
419   if (!v4_fallback &&
420      (sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)) >= 0)
421   {
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)
424     {
425 #ifdef _MSC_VER
426       CStdString sock_err = WUSysMsg(WSAGetLastError());
427 #else
428       CStdString sock_err = strerror(errno);
429 #endif
430       CLog::Log(LOGWARNING, "%s Server: Only IPv6 supported (%s)", callerName, sock_err.c_str());
431     }
432   }
433   else
434   {
435     v4_fallback = true;
436     sock = socket(PF_INET, SOCK_STREAM, 0);
437   }
438   
439   if (sock == INVALID_SOCKET)
440   {
441     CLog::Log(LOGERROR, "%s Server: Failed to create serversocket", callerName);
442     return INVALID_SOCKET;
443   }
444
445   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
446   
447   if (v4_fallback)
448   {
449     addr.ss_family = AF_INET;
450     s4 = (struct sockaddr_in *) &addr;
451     s4->sin_port = htons(port);
452
453     if (bindLocal)
454       s4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
455     else
456       s4->sin_addr.s_addr = htonl(INADDR_ANY);
457   }
458   else
459   {
460     addr.ss_family = AF_INET6;
461     s6 = (struct sockaddr_in6 *) &addr;
462     s6->sin6_port = htons(port);
463
464     if (bindLocal)
465       s6->sin6_addr = in6addr_loopback;
466     else
467       s6->sin6_addr = in6addr_any;
468   }
469
470   if (::bind( sock, (struct sockaddr *) &addr,
471             (addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) :
472                                            sizeof(struct sockaddr_in)  ) < 0)
473   {
474     closesocket(sock);
475     CLog::Log(LOGERROR, "%s Server: Failed to bind serversocket", callerName);
476     return INVALID_SOCKET;
477   }
478
479   if (listen(sock, backlog) < 0)
480   {
481     closesocket(sock);
482     CLog::Log(LOGERROR, "%s Server: Failed to set listen", callerName);
483     return INVALID_SOCKET;
484   }
485
486   return sock;
487 }
488