eaebe415ae8321803a8dd3296dff7be15ff63dba
[vuplus_xbmc] / xbmc / network / linux / NetworkLinux.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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 <sys/ioctl.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #if defined(TARGET_LINUX)
26   #include <linux/if.h>
27   #include <linux/wireless.h>
28   #include <linux/sockios.h>
29 #endif
30 #ifdef TARGET_ANDROID
31 #include "android/bionic_supplement/bionic_supplement.h"
32 #include "sys/system_properties.h"
33 #include <sys/wait.h>
34 #endif
35 #include <errno.h>
36 #include <resolv.h>
37 #if defined(TARGET_DARWIN)
38   #include <sys/sockio.h>
39   #include <net/if.h>
40   #include <net/if_dl.h>
41 #if defined(TARGET_DARWIN_OSX)
42   #include <net/if_types.h>
43   #include <net/route.h>
44   #include <netinet/if_ether.h>
45 #else //IOS
46   #include "network/osx/ioshacks.h"
47 #endif
48   #include <ifaddrs.h>
49 #elif defined(TARGET_FREEBSD)
50   #include <sys/sockio.h>
51   #include <sys/wait.h>
52   #include <net/if.h>
53   #include <net/if_arp.h>
54   #include <net/if_dl.h>
55   #include <ifaddrs.h>
56   #include <net/route.h>
57   #include <netinet/if_ether.h>
58 #else
59   #include <net/if_arp.h>
60 #endif
61 #include "PlatformDefs.h"
62 #include "NetworkLinux.h"
63 #include "Util.h"
64 #include "utils/StringUtils.h"
65 #include "utils/log.h"
66
67 using namespace std;
68
69 CNetworkInterfaceLinux::CNetworkInterfaceLinux(CNetworkLinux* network, CStdString interfaceName, char interfaceMacAddrRaw[6])
70
71 {
72    m_network = network;
73    m_interfaceName = interfaceName;
74    m_interfaceMacAdr.Format("%02X:%02X:%02X:%02X:%02X:%02X",
75                   (uint8_t)interfaceMacAddrRaw[0],
76                   (uint8_t)interfaceMacAddrRaw[1],
77                   (uint8_t)interfaceMacAddrRaw[2],
78                   (uint8_t)interfaceMacAddrRaw[3],
79                   (uint8_t)interfaceMacAddrRaw[4],
80                   (uint8_t)interfaceMacAddrRaw[5]);
81    memcpy(m_interfaceMacAddrRaw, interfaceMacAddrRaw, sizeof(m_interfaceMacAddrRaw));
82 }
83
84 CNetworkInterfaceLinux::~CNetworkInterfaceLinux(void)
85 {
86 }
87
88 CStdString& CNetworkInterfaceLinux::GetName(void)
89 {
90    return m_interfaceName;
91 }
92
93 bool CNetworkInterfaceLinux::IsWireless()
94 {
95 #if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD)
96   return false;
97 #else
98   struct iwreq wrq;
99    strcpy(wrq.ifr_name, m_interfaceName.c_str());
100    if (ioctl(m_network->GetSocket(), SIOCGIWNAME, &wrq) < 0)
101       return false;
102 #endif
103
104    return true;
105 }
106
107 bool CNetworkInterfaceLinux::IsEnabled()
108 {
109    struct ifreq ifr;
110    strcpy(ifr.ifr_name, m_interfaceName.c_str());
111    if (ioctl(m_network->GetSocket(), SIOCGIFFLAGS, &ifr) < 0)
112       return false;
113
114    return ((ifr.ifr_flags & IFF_UP) == IFF_UP);
115 }
116
117 bool CNetworkInterfaceLinux::IsConnected()
118 {
119    struct ifreq ifr;
120    int zero = 0;
121    memset(&ifr,0,sizeof(struct ifreq));
122    strcpy(ifr.ifr_name, m_interfaceName.c_str());
123    if (ioctl(m_network->GetSocket(), SIOCGIFFLAGS, &ifr) < 0)
124       return false;
125
126    // ignore loopback
127    int iRunning = ( (ifr.ifr_flags & IFF_RUNNING) && (!(ifr.ifr_flags & IFF_LOOPBACK)));
128
129    if (ioctl(m_network->GetSocket(), SIOCGIFADDR, &ifr) < 0)
130       return false;
131
132    // return only interfaces which has ip address
133    return iRunning && (0 != memcmp(ifr.ifr_addr.sa_data+sizeof(short), &zero, sizeof(int)));
134 }
135
136 CStdString CNetworkInterfaceLinux::GetMacAddress()
137 {
138   return m_interfaceMacAdr;
139 }
140
141 void CNetworkInterfaceLinux::GetMacAddressRaw(char rawMac[6])
142 {
143   memcpy(rawMac, m_interfaceMacAddrRaw, 6);
144 }
145
146 CStdString CNetworkInterfaceLinux::GetCurrentIPAddress(void)
147 {
148    CStdString result = "";
149
150    struct ifreq ifr;
151    strcpy(ifr.ifr_name, m_interfaceName.c_str());
152    ifr.ifr_addr.sa_family = AF_INET;
153    if (ioctl(m_network->GetSocket(), SIOCGIFADDR, &ifr) >= 0)
154    {
155       result = inet_ntoa((*((struct sockaddr_in *)&ifr.ifr_addr)).sin_addr);
156    }
157
158    return result;
159 }
160
161 CStdString CNetworkInterfaceLinux::GetCurrentNetmask(void)
162 {
163    CStdString result = "";
164
165    struct ifreq ifr;
166    strcpy(ifr.ifr_name, m_interfaceName.c_str());
167    ifr.ifr_addr.sa_family = AF_INET;
168    if (ioctl(m_network->GetSocket(), SIOCGIFNETMASK, &ifr) >= 0)
169    {
170       result = inet_ntoa((*((struct sockaddr_in*)&ifr.ifr_addr)).sin_addr);
171    }
172
173    return result;
174 }
175
176 CStdString CNetworkInterfaceLinux::GetCurrentWirelessEssId(void)
177 {
178    CStdString result = "";
179
180 #if defined(TARGET_LINUX)
181    char essid[IW_ESSID_MAX_SIZE + 1];
182    memset(&essid, 0, sizeof(essid));
183
184    struct iwreq wrq;
185    strcpy(wrq.ifr_name,  m_interfaceName.c_str());
186    wrq.u.essid.pointer = (caddr_t) essid;
187    wrq.u.essid.length = IW_ESSID_MAX_SIZE;
188    wrq.u.essid.flags = 0;
189    if (ioctl(m_network->GetSocket(), SIOCGIWESSID, &wrq) >= 0)
190    {
191       result = essid;
192    }
193 #endif
194
195    return result;
196 }
197
198 CStdString CNetworkInterfaceLinux::GetCurrentDefaultGateway(void)
199 {
200    CStdString result = "";
201
202 #if defined(TARGET_DARWIN)
203   FILE* pipe = popen("echo \"show State:/Network/Global/IPv4\" | scutil | grep Router", "r");
204   if (pipe)
205   {
206     CStdString tmpStr;
207     char buffer[256] = {'\0'};
208     if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe))
209     {
210       tmpStr = buffer;
211       result = tmpStr.Mid(11);
212     }
213     else
214     {
215       CLog::Log(LOGWARNING, "Unable to determine gateway");
216     }
217     pclose(pipe);
218   }
219 #elif defined(TARGET_FREEBSD)
220    size_t needed;
221    int mib[6];
222    char *buf, *next, *lim;
223    char line[16];
224    struct rt_msghdr *rtm;
225    struct sockaddr *sa;
226    struct sockaddr_in *sockin;
227
228    mib[0] = CTL_NET;
229    mib[1] = PF_ROUTE;
230    mib[2] = 0;
231    mib[3] = 0;
232    mib[4] = NET_RT_DUMP;
233    mib[5] = 0;
234    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
235       return result;
236
237    if ((buf = (char *)malloc(needed)) == NULL)
238       return result;
239
240    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
241       free(buf);
242       return result;
243    }
244
245    lim  = buf + needed;
246    for (next = buf; next < lim; next += rtm->rtm_msglen) {
247       rtm = (struct rt_msghdr *)next;
248       sa = (struct sockaddr *)(rtm + 1);
249       sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);       
250       sockin = (struct sockaddr_in *)sa;
251       if (inet_ntop(AF_INET, &sockin->sin_addr.s_addr,
252          line, sizeof(line)) == NULL) {
253             free(buf);
254             return result;
255           }
256           result = line;
257       break;
258    }
259    free(buf);
260 #else
261    FILE* fp = fopen("/proc/net/route", "r");
262    if (!fp)
263    {
264      // TBD: Error
265      return result;
266    }
267
268    char* line = NULL;
269    char iface[16];
270    char dst[128];
271    char gateway[128];
272    size_t linel = 0;
273    int n;
274    int linenum = 0;
275    while (getdelim(&line, &linel, '\n', fp) > 0)
276    {
277       // skip first two lines
278       if (linenum++ < 1)
279          continue;
280
281       // search where the word begins
282       n = sscanf(line,  "%16s %128s %128s",
283          iface, dst, gateway);
284
285       if (n < 3)
286          continue;
287
288       if (strcmp(iface, m_interfaceName.c_str()) == 0 &&
289           strcmp(dst, "00000000") == 0 &&
290           strcmp(gateway, "00000000") != 0)
291       {
292          unsigned char gatewayAddr[4];
293          int len = CNetwork::ParseHex(gateway, gatewayAddr);
294          if (len == 4)
295          {
296             struct in_addr in;
297             in.s_addr = (gatewayAddr[0] << 24) | (gatewayAddr[1] << 16) |
298                         (gatewayAddr[2] << 8) | (gatewayAddr[3]);
299             result = inet_ntoa(in);
300             break;
301          }
302       }
303    }
304    free(line);
305    fclose(fp);
306 #endif
307
308    return result;
309 }
310
311 CNetworkLinux::CNetworkLinux(void)
312 {
313    m_sock = socket(AF_INET, SOCK_DGRAM, 0);
314    queryInterfaceList();
315 }
316
317 CNetworkLinux::~CNetworkLinux(void)
318 {
319   if (m_sock != -1)
320     close(CNetworkLinux::m_sock);
321
322   vector<CNetworkInterface*>::iterator it = m_interfaces.begin();
323   while(it != m_interfaces.end())
324   {
325     CNetworkInterface* nInt = *it;
326     delete nInt;
327     it = m_interfaces.erase(it);
328   }
329 }
330
331 std::vector<CNetworkInterface*>& CNetworkLinux::GetInterfaceList(void)
332 {
333    return m_interfaces;
334 }
335
336 // Overwrite the GetFirstConnectedInterface and requery
337 // the interface list if no connected device is found
338 // this fixes a bug when no network is available after first start of xbmc
339 // and the interface comes up during runtime
340 CNetworkInterface* CNetworkLinux::GetFirstConnectedInterface(void)
341 {
342     CNetworkInterface *pNetIf=CNetwork::GetFirstConnectedInterface();
343     
344     // no connected Interfaces found? - requeryInterfaceList
345     if (!pNetIf)
346     {
347         CLog::Log(LOGDEBUG,"%s no connected interface found - requery list",__FUNCTION__);        
348         queryInterfaceList();        
349         //retry finding a connected if
350         pNetIf = CNetwork::GetFirstConnectedInterface();
351     }
352     
353     return pNetIf;
354 }
355
356
357 void CNetworkLinux::GetMacAddress(CStdString interfaceName, char rawMac[6])
358 {
359   memset(rawMac, 0, 6);
360 #if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD)
361
362 #if !defined(IFT_ETHER)
363 #define IFT_ETHER 0x6/* Ethernet CSMACD */
364 #endif
365   const struct sockaddr_dl* dlAddr = NULL;
366   const uint8_t * base = NULL;
367   // Query the list of interfaces.
368   struct ifaddrs *list;
369   struct ifaddrs *interface;
370
371   if( getifaddrs(&list) < 0 )
372   {
373     return;
374   }
375
376   for(interface = list; interface != NULL; interface = interface->ifa_next)
377   {
378     if(CStdString(interface->ifa_name).Equals(interfaceName))
379     {
380       if ( (interface->ifa_addr->sa_family == AF_LINK) && (((const struct sockaddr_dl *) interface->ifa_addr)->sdl_type == IFT_ETHER) ) 
381       {
382         dlAddr = (const struct sockaddr_dl *) interface->ifa_addr;
383         base = (const uint8_t *) &dlAddr->sdl_data[dlAddr->sdl_nlen];
384
385         if( dlAddr->sdl_alen > 5 )
386         {
387           memcpy(rawMac, base, 6);
388         }
389       }
390       break;
391     }
392   }
393
394   freeifaddrs(list);
395
396 #else
397
398    struct ifreq ifr;
399    strcpy(ifr.ifr_name, interfaceName.c_str());
400    if (ioctl(GetSocket(), SIOCGIFHWADDR, &ifr) >= 0)
401    {
402       memcpy(rawMac, ifr.ifr_hwaddr.sa_data, 6);
403    }
404 #endif
405 }
406
407 void CNetworkLinux::queryInterfaceList()
408 {
409   char macAddrRaw[6];
410   m_interfaces.clear();
411
412 #if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD)
413
414    // Query the list of interfaces.
415    struct ifaddrs *list;
416    if (getifaddrs(&list) < 0)
417      return;
418
419    struct ifaddrs *cur;
420    for(cur = list; cur != NULL; cur = cur->ifa_next)
421    {
422      if(cur->ifa_addr->sa_family != AF_INET)
423        continue;
424
425      GetMacAddress(cur->ifa_name, macAddrRaw);
426      // Add the interface.
427      m_interfaces.push_back(new CNetworkInterfaceLinux(this, cur->ifa_name, macAddrRaw));
428    }
429
430    freeifaddrs(list);
431
432 #else
433    FILE* fp = fopen("/proc/net/dev", "r");
434    if (!fp)
435    {
436      // TBD: Error
437      return;
438    }
439
440    char* line = NULL;
441    size_t linel = 0;
442    int n;
443    char* p;
444    int linenum = 0;
445    while (getdelim(&line, &linel, '\n', fp) > 0)
446    {
447       // skip first two lines
448       if (linenum++ < 2)
449          continue;
450
451     // search where the word begins
452       p = line;
453       while (isspace(*p))
454       ++p;
455
456       // read word until :
457       n = strcspn(p, ": \t");
458       p[n] = 0;
459
460       // save the result
461       CStdString interfaceName = p;
462       GetMacAddress(interfaceName, macAddrRaw);
463       m_interfaces.push_back(new CNetworkInterfaceLinux(this, interfaceName, macAddrRaw));
464    }
465    free(line);
466    fclose(fp);
467 #endif
468 }
469
470 std::vector<CStdString> CNetworkLinux::GetNameServers(void)
471 {
472    std::vector<CStdString> result;
473
474 #if defined(TARGET_DARWIN)
475   //only finds the primary dns (0 :)
476   FILE* pipe = popen("scutil --dns | grep \"nameserver\\[0\\]\" | tail -n1", "r");
477   if (pipe)
478   {
479     CStdString tmpStr;
480     char buffer[256] = {'\0'};
481     if (fread(buffer, sizeof(char), sizeof(buffer), pipe) > 0 && !ferror(pipe))
482     {
483       tmpStr = buffer;
484       result.push_back(tmpStr.Mid(17));
485     }
486     else
487     {
488       CLog::Log(LOGWARNING, "Unable to determine nameserver");
489     }
490     pclose(pipe);
491   } 
492 #elif defined(TARGET_ANDROID)
493   char nameserver[PROP_VALUE_MAX];
494
495   if (__system_property_get("net.dns1",nameserver))
496     result.push_back(nameserver);
497   if (__system_property_get("net.dns2",nameserver))
498     result.push_back(nameserver);
499   if (__system_property_get("net.dns3",nameserver))
500     result.push_back(nameserver);
501
502   if (!result.size())
503        CLog::Log(LOGWARNING, "Unable to determine nameserver");
504 #else
505    res_init();
506
507    for (int i = 0; i < _res.nscount; i ++)
508    {
509       CStdString ns = inet_ntoa(((struct sockaddr_in *)&_res.nsaddr_list[i])->sin_addr);
510       result.push_back(ns);
511    }
512 #endif
513    return result;
514 }
515
516 void CNetworkLinux::SetNameServers(std::vector<CStdString> nameServers)
517 {
518 #if !defined(TARGET_ANDROID)
519    FILE* fp = fopen("/etc/resolv.conf", "w");
520    if (fp != NULL)
521    {
522       for (unsigned int i = 0; i < nameServers.size(); i++)
523       {
524          fprintf(fp, "nameserver %s\n", nameServers[i].c_str());
525       }
526       fclose(fp);
527    }
528    else
529    {
530       // TODO:
531    }
532 #endif
533 }
534
535 bool CNetworkLinux::PingHost(unsigned long remote_ip, unsigned int timeout_ms)
536 {
537   char cmd_line [64];
538
539   struct in_addr host_ip; 
540   host_ip.s_addr = remote_ip;
541
542 #if defined (TARGET_DARWIN_IOS) // no timeout option available
543   sprintf(cmd_line, "ping -c 1 %s", inet_ntoa(host_ip));
544 #elif defined (TARGET_DARWIN) || defined (TARGET_FREEBSD)
545   sprintf(cmd_line, "ping -c 1 -t %d %s", timeout_ms / 1000 + (timeout_ms % 1000) != 0, inet_ntoa(host_ip));
546 #else
547   sprintf(cmd_line, "ping -c 1 -w %d %s", timeout_ms / 1000 + (timeout_ms % 1000) != 0, inet_ntoa(host_ip));
548 #endif
549
550   int status = system (cmd_line);
551
552   int result = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
553
554   // http://linux.about.com/od/commands/l/blcmdl8_ping.htm ;
555   // 0 reply
556   // 1 no reply
557   // else some error
558
559   if (result < 0 || result > 1)
560     CLog::Log(LOGERROR, "Ping fail : status = %d, errno = %d : '%s'", status, errno, cmd_line);
561
562   return result == 0;
563 }
564
565 #if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD)
566 bool CNetworkInterfaceLinux::GetHostMacAddress(unsigned long host_ip, CStdString& mac)
567 {
568   bool ret = false;
569   size_t needed;
570   char *buf, *next;
571   struct rt_msghdr *rtm;
572   struct sockaddr_inarp *sin;
573   struct sockaddr_dl *sdl;
574   int mib[6];
575   
576   mac = "";
577   
578   mib[0] = CTL_NET;
579   mib[1] = PF_ROUTE;
580   mib[2] = 0;
581   mib[3] = AF_INET;
582   mib[4] = NET_RT_FLAGS;
583   mib[5] = RTF_LLINFO;
584   
585   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &needed, NULL, 0) == 0)
586   {   
587     buf = (char*)malloc(needed);
588     if (buf)
589     {      
590       if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &needed, NULL, 0) == 0)
591       {        
592         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) 
593         {
594           
595           rtm = (struct rt_msghdr *)next;
596           sin = (struct sockaddr_inarp *)(rtm + 1);
597           sdl = (struct sockaddr_dl *)(sin + 1);
598           
599           if (host_ip != sin->sin_addr.s_addr || sdl->sdl_alen < 6)
600             continue;
601           
602           u_char *cp = (u_char*)LLADDR(sdl);
603           
604           mac.Format("%02X:%02X:%02X:%02X:%02X:%02X",
605                      cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
606           ret = true;
607           break;
608         }
609       }
610       free(buf);
611     }
612   }
613   return ret;
614 }
615 #else
616 bool CNetworkInterfaceLinux::GetHostMacAddress(unsigned long host_ip, CStdString& mac)
617 {
618   struct arpreq areq;
619   struct sockaddr_in* sin;
620
621   memset(&areq, 0x0, sizeof(areq));
622
623   sin = (struct sockaddr_in *) &areq.arp_pa;
624   sin->sin_family = AF_INET;
625   sin->sin_addr.s_addr = host_ip;
626
627   sin = (struct sockaddr_in *) &areq.arp_ha;
628   sin->sin_family = ARPHRD_ETHER;
629
630   strncpy(areq.arp_dev, m_interfaceName.c_str(), sizeof(areq.arp_dev));
631   areq.arp_dev[sizeof(areq.arp_dev)-1] = '\0';
632
633   int result = ioctl (m_network->GetSocket(), SIOCGARP, (caddr_t) &areq);
634
635   if (result != 0)
636   {
637 //  CLog::Log(LOGERROR, "%s - GetHostMacAddress/ioctl failed with errno (%d)", __FUNCTION__, errno);
638     return false;
639   }
640
641   struct sockaddr* res = &areq.arp_ha;
642   mac.Format("%02X:%02X:%02X:%02X:%02X:%02X", 
643     (uint8_t) res->sa_data[0], (uint8_t) res->sa_data[1], (uint8_t) res->sa_data[2], 
644     (uint8_t) res->sa_data[3], (uint8_t) res->sa_data[4], (uint8_t) res->sa_data[5]);
645
646   for (int i=0; i<6; ++i)
647     if (res->sa_data[i])
648       return true;
649
650   return false;
651 }
652 #endif
653
654 std::vector<NetworkAccessPoint> CNetworkInterfaceLinux::GetAccessPoints(void)
655 {
656    std::vector<NetworkAccessPoint> result;
657
658    if (!IsWireless())
659       return result;
660
661 #if defined(TARGET_LINUX)
662    // Query the wireless extension's version number. It will help us when we
663    // parse the resulting events
664    struct iwreq iwr;
665    char rangebuffer[sizeof(iw_range) * 2];    /* Large enough */
666    struct iw_range*  range = (struct iw_range*) rangebuffer;
667
668    memset(rangebuffer, 0, sizeof(rangebuffer));
669    iwr.u.data.pointer = (caddr_t) rangebuffer;
670    iwr.u.data.length = sizeof(rangebuffer);
671    iwr.u.data.flags = 0;
672    strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ);
673    iwr.ifr_name[IFNAMSIZ - 1] = 0;
674    if (ioctl(m_network->GetSocket(), SIOCGIWRANGE, &iwr) < 0)
675    {
676       CLog::Log(LOGWARNING, "%-8.16s  Driver has no Wireless Extension version information.",
677          GetName().c_str());
678       return result;
679    }
680
681    // Scan for wireless access points
682    memset(&iwr, 0, sizeof(iwr));
683    strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ);
684    iwr.ifr_name[IFNAMSIZ - 1] = 0;
685    if (ioctl(m_network->GetSocket(), SIOCSIWSCAN, &iwr) < 0)
686    {
687       // Triggering scanning is a privileged operation (root only)
688       if (errno == EPERM)
689          CLog::Log(LOGWARNING, "Cannot initiate wireless scan: ioctl[SIOCSIWSCAN]: %s. Try running as root", strerror(errno));
690       else
691          CLog::Log(LOGWARNING, "Cannot initiate wireless scan: ioctl[SIOCSIWSCAN]: %s", strerror(errno));
692       return result;
693    }
694
695    // Get the results of the scanning. Three scenarios:
696    //    1. There's not enough room in the result buffer (E2BIG)
697    //    2. The scanning is not complete (EAGAIN) and we need to try again. We cap this with 15 seconds.
698    //    3. We're good.
699    int duration = 0; // ms
700    unsigned char* res_buf = NULL;
701    int res_buf_len = IW_SCAN_MAX_DATA;
702    while (duration < 15000)
703    {
704       if (!res_buf)
705          res_buf = (unsigned char*) malloc(res_buf_len);
706
707       if (res_buf == NULL)
708       {
709          CLog::Log(LOGWARNING, "Cannot alloc memory for wireless scanning");
710          return result;
711       }
712
713       strncpy(iwr.ifr_name, GetName().c_str(), IFNAMSIZ);
714       iwr.ifr_name[IFNAMSIZ - 1] = 0;
715       iwr.u.data.pointer = res_buf;
716       iwr.u.data.length = res_buf_len;
717       iwr.u.data.flags = 0;
718       int x = ioctl(m_network->GetSocket(), SIOCGIWSCAN, &iwr);
719       if (x == 0)
720          break;
721
722       if (errno == E2BIG && res_buf_len < 100000)
723       {
724          free(res_buf);
725          res_buf = NULL;
726          res_buf_len *= 2;
727          CLog::Log(LOGDEBUG, "Scan results did not fit - trying larger buffer (%lu bytes)",
728                         (unsigned long) res_buf_len);
729       }
730       else if (errno == EAGAIN)
731       {
732          usleep(250000); // sleep for 250ms
733          duration += 250;
734       }
735       else
736       {
737          CLog::Log(LOGWARNING, "Cannot get wireless scan results: ioctl[SIOCGIWSCAN]: %s", strerror(errno));
738          free(res_buf);
739          return result;
740       }
741    }
742
743    size_t len = iwr.u.data.length;           // total length of the wireless events from the scan results
744    unsigned char* pos = res_buf;             // pointer to the current event (about 10 per wireless network)
745    unsigned char* end = res_buf + len;       // marks the end of the scan results
746    unsigned char* custom;                    // pointer to the event payload
747    struct iw_event iwe_buf, *iwe = &iwe_buf; // buffer to hold individual events
748
749    CStdString essId;
750    CStdString macAddress;
751    int signalLevel = 0;
752    EncMode encryption = ENC_NONE;
753    int channel = 0;
754
755    while (pos + IW_EV_LCP_LEN <= end)
756    {
757       /* Event data may be unaligned, so make a local, aligned copy
758        * before processing. */
759
760       // copy event prefix (size of event minus IOCTL fixed payload)
761       memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
762       if (iwe->len <= IW_EV_LCP_LEN)
763          break;
764
765       // if the payload is nontrivial (i.e. > 16 octets) assume it comes after a pointer
766       custom = pos + IW_EV_POINT_LEN;
767       if (range->we_version_compiled > 18 &&
768           (iwe->cmd == SIOCGIWESSID ||
769            iwe->cmd == SIOCGIWENCODE ||
770            iwe->cmd == IWEVGENIE ||
771            iwe->cmd == IWEVCUSTOM))
772       {
773          /* Wireless extensions v19 removed the pointer from struct iw_point */
774          char *data_pos = (char *) &iwe_buf.u.data.length;
775          int data_len = data_pos - (char *) &iwe_buf;
776          memcpy(data_pos, pos + IW_EV_LCP_LEN, sizeof(struct iw_event) - data_len);
777       }
778       else
779       {
780          // copy the rest of the event and point custom toward the payload offset
781          memcpy(&iwe_buf, pos, sizeof(struct iw_event));
782          custom += IW_EV_POINT_OFF;
783       }
784
785       // Interpret the payload based on event type. Each access point generates ~12 different events
786       switch (iwe->cmd)
787       {
788          // Get access point MAC addresses
789          case SIOCGIWAP:
790          {
791             // This event marks a new access point, so push back the old information
792             if (!macAddress.IsEmpty())
793                result.push_back(NetworkAccessPoint(essId, macAddress, signalLevel, encryption, channel));
794             unsigned char* mac = (unsigned char*)iwe->u.ap_addr.sa_data;
795             // macAddress is big-endian, write in byte chunks
796             macAddress.Format("%02x-%02x-%02x-%02x-%02x-%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
797             // Reset the remaining fields
798             essId = "";
799             encryption = ENC_NONE;
800             signalLevel = 0;
801             channel = 0;
802             break;
803          }
804
805          // Get operation mode
806          case SIOCGIWMODE:
807          {
808             // Ignore Ad-Hoc networks (1 is the magic number for this)
809             if (iwe->u.mode == 1)
810                macAddress = "";
811             break;
812          }
813
814          // Get ESSID
815          case SIOCGIWESSID:
816          {
817             char essid[IW_ESSID_MAX_SIZE+1];
818             memset(essid, '\0', sizeof(essid));
819             if ((custom) && (iwe->u.essid.length))
820             {
821                memcpy(essid, custom, iwe->u.essid.length);
822                essId = essid;
823             }
824             break;
825          }
826
827          // Quality part of statistics
828          case IWEVQUAL:
829          {
830             // u.qual.qual is scaled to a vendor-specific RSSI_Max, so use u.qual.level
831             signalLevel = iwe->u.qual.level - 0x100; // and remember we use 8-bit arithmetic
832             break;
833          }
834
835          // Get channel/frequency (Hz)
836          // This gets called twice per network, what's the difference between the two?
837          case SIOCGIWFREQ:
838          {
839             float freq = ((float)iwe->u.freq.m) * pow(10.0, iwe->u.freq.e);
840             if (freq > 1000)
841                channel = NetworkAccessPoint::FreqToChannel(freq);
842             else
843                channel = (int)freq; // Some drivers report channel instead of frequency
844             break;
845          }
846
847          // Get encoding token & mode
848          case SIOCGIWENCODE:
849          {
850             if (!(iwe->u.data.flags & IW_ENCODE_DISABLED) && encryption == ENC_NONE)
851                encryption = ENC_WEP;
852             break;
853          }
854
855          // Generic IEEE 802.11 information element (IE) for WPA, RSN, WMM, ...
856          case IWEVGENIE:
857          {
858             int offset = 0;
859             // Loop on each IE, each IE is minimum 2 bytes
860             while (offset <= (iwe_buf.u.data.length - 2))
861             {
862                switch (custom[offset])
863                {
864                   case 0xdd: /* WPA1 */
865                      if (encryption != ENC_WPA2)
866                         encryption = ENC_WPA;
867                      break;
868                   case 0x30: /* WPA2 */
869                      encryption = ENC_WPA2;
870                }
871                // Skip over this IE to the next one in the list
872                offset += custom[offset+1] + 2;
873             }
874          }
875       }
876
877       pos += iwe->len;
878    }
879
880    if (!macAddress.IsEmpty())
881       result.push_back(NetworkAccessPoint(essId, macAddress, signalLevel, encryption, channel));
882
883    free(res_buf);
884    res_buf = NULL;
885 #endif
886
887    return result;
888 }
889
890 void CNetworkInterfaceLinux::GetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
891 {
892    ipAddress = "0.0.0.0";
893    networkMask = "0.0.0.0";
894    defaultGateway = "0.0.0.0";
895    essId = "";
896    key = "";
897    encryptionMode = ENC_NONE;
898    assignment = NETWORK_DISABLED;
899
900 #if defined(TARGET_LINUX)
901    FILE* fp = fopen("/etc/network/interfaces", "r");
902    if (!fp)
903    {
904       // TODO
905       return;
906    }
907
908    char* line = NULL;
909    size_t linel = 0;
910    CStdString s;
911    bool foundInterface = false;
912
913    while (getdelim(&line, &linel, '\n', fp) > 0)
914    {
915       std::vector<std::string> tokens;
916
917       s = line;
918       s.TrimLeft(" \t").TrimRight(" \n");
919
920       // skip comments
921       if (s.length() == 0 || s.GetAt(0) == '#')
922          continue;
923
924       // look for "iface <interface name> inet"
925       StringUtils::Tokenize(s, tokens, " ");
926       if (!foundInterface &&
927           tokens.size() >=3 &&
928           StringUtils::EqualsNoCase(tokens[0], "iface") &&
929           StringUtils::EqualsNoCase(tokens[1], GetName()) &&
930           StringUtils::EqualsNoCase(tokens[2], "inet"))
931       {
932          if (StringUtils::EqualsNoCase(tokens[3], "dhcp"))
933          {
934             assignment = NETWORK_DHCP;
935             foundInterface = true;
936          }
937          if (StringUtils::EqualsNoCase(tokens[3], "static"))
938          {
939             assignment = NETWORK_STATIC;
940             foundInterface = true;
941          }
942       }
943
944       if (foundInterface && tokens.size() == 2)
945       {
946          if (StringUtils::EqualsNoCase(tokens[0], "address")) ipAddress = tokens[1];
947          else if (StringUtils::EqualsNoCase(tokens[0], "netmask")) networkMask = tokens[1];
948          else if (StringUtils::EqualsNoCase(tokens[0], "gateway")) defaultGateway = tokens[1];
949          else if (StringUtils::EqualsNoCase(tokens[0], "wireless-essid")) essId = tokens[1];
950          else if (StringUtils::EqualsNoCase(tokens[0], "wireless-key"))
951          {
952             key = tokens[1];
953             if (key.length() > 2 && key[0] == 's' && key[1] == ':')
954                key.erase(0, 2);
955             encryptionMode = ENC_WEP;
956          }
957          else if (StringUtils::EqualsNoCase(tokens[0], "wpa-ssid")) essId = tokens[1];
958          else if (StringUtils::EqualsNoCase(tokens[0], "wpa-proto") && StringUtils::EqualsNoCase(tokens[1], "WPA")) encryptionMode = ENC_WPA;
959          else if (StringUtils::EqualsNoCase(tokens[0], "wpa-proto") && StringUtils::EqualsNoCase(tokens[1], "WPA2")) encryptionMode = ENC_WPA2;
960          else if (StringUtils::EqualsNoCase(tokens[0], "wpa-psk")) key = tokens[1];
961          else if (StringUtils::EqualsNoCase(tokens[0], "auto") || StringUtils::EqualsNoCase(tokens[0], "iface") || StringUtils::EqualsNoCase(tokens[0], "mapping")) break;
962       }
963    }
964    free(line);
965
966    // Fallback in case wpa-proto is not set
967    if (key != "" && encryptionMode == ENC_NONE)
968       encryptionMode = ENC_WPA;
969
970    fclose(fp);
971 #endif
972 }
973
974 void CNetworkInterfaceLinux::SetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
975 {
976 #if defined(TARGET_LINUX)
977    FILE* fr = fopen("/etc/network/interfaces", "r");
978    if (!fr)
979    {
980       // TODO
981       return;
982    }
983
984    FILE* fw = fopen("/tmp/interfaces.temp", "w");
985    if (!fw)
986    {
987       // TODO
988       fclose(fr);
989       return;
990    }
991
992    char* line = NULL;
993    size_t linel = 0;
994    CStdString s;
995    bool foundInterface = false;
996    bool dataWritten = false;
997
998    while (getdelim(&line, &linel, '\n', fr) > 0)
999    {
1000       std::vector<std::string> tokens;
1001
1002       s = line;
1003       s.TrimLeft(" \t").TrimRight(" \n");
1004
1005       // skip comments
1006       if (!foundInterface && (s.length() == 0 || s.GetAt(0) == '#'))
1007       {
1008         fprintf(fw, "%s", line);
1009         continue;
1010       }
1011
1012       // look for "iface <interface name> inet"
1013       StringUtils::Tokenize(s, tokens, " ");
1014       if (tokens.size() == 2 &&
1015           StringUtils::EqualsNoCase(tokens[0], "auto") &&
1016           StringUtils::EqualsNoCase(tokens[1], GetName()))
1017       {
1018          continue;
1019       }
1020       else if (!foundInterface &&
1021           tokens.size() == 4 &&
1022           StringUtils::EqualsNoCase(tokens[0], "iface") &&
1023           StringUtils::EqualsNoCase(tokens[1], GetName()) &&
1024           StringUtils::EqualsNoCase(tokens[2], "inet"))
1025       {
1026          foundInterface = true;
1027          WriteSettings(fw, assignment, ipAddress, networkMask, defaultGateway, essId, key, encryptionMode);
1028          dataWritten = true;
1029       }
1030       else if (foundInterface &&
1031                tokens.size() == 4 &&
1032                StringUtils::EqualsNoCase(tokens[0], "iface"))
1033       {
1034         foundInterface = false;
1035         fprintf(fw, "%s", line);
1036       }
1037       else if (!foundInterface)
1038       {
1039         fprintf(fw, "%s", line);
1040       }
1041    }
1042    free(line);
1043
1044    if (!dataWritten && assignment != NETWORK_DISABLED)
1045    {
1046       fprintf(fw, "\n");
1047       WriteSettings(fw, assignment, ipAddress, networkMask, defaultGateway, essId, key, encryptionMode);
1048    }
1049
1050    fclose(fr);
1051    fclose(fw);
1052
1053    // Rename the file
1054    if (rename("/tmp/interfaces.temp", "/etc/network/interfaces") < 0)
1055    {
1056       // TODO
1057       return;
1058    }
1059
1060    std::string cmd = "/sbin/ifdown " + GetName();
1061    if (system(cmd.c_str()) != 0)
1062      CLog::Log(LOGERROR, "Unable to stop interface %s", GetName().c_str());
1063    else
1064      CLog::Log(LOGINFO, "Stopped interface %s", GetName().c_str());
1065
1066    if (assignment != NETWORK_DISABLED)
1067    {
1068       cmd = "/sbin/ifup " + GetName();
1069       if (system(cmd.c_str()) != 0)
1070         CLog::Log(LOGERROR, "Unable to start interface %s", GetName().c_str());
1071       else
1072         CLog::Log(LOGINFO, "Started interface %s", GetName().c_str());
1073    }
1074 #endif
1075 }
1076
1077 void CNetworkInterfaceLinux::WriteSettings(FILE* fw, NetworkAssignment assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
1078 {
1079    if (assignment == NETWORK_DHCP)
1080    {
1081       fprintf(fw, "iface %s inet dhcp\n", GetName().c_str());
1082    }
1083    else if (assignment == NETWORK_STATIC)
1084    {
1085       fprintf(fw, "iface %s inet static\n", GetName().c_str());
1086       fprintf(fw, "  address %s\n", ipAddress.c_str());
1087       fprintf(fw, "  netmask %s\n", networkMask.c_str());
1088       fprintf(fw, "  gateway %s\n", defaultGateway.c_str());
1089    }
1090
1091    if (assignment != NETWORK_DISABLED && IsWireless())
1092    {
1093       if (encryptionMode == ENC_NONE)
1094       {
1095          fprintf(fw, "  wireless-essid %s\n", essId.c_str());
1096       }
1097       else if (encryptionMode == ENC_WEP)
1098       {
1099          fprintf(fw, "  wireless-essid %s\n", essId.c_str());
1100          fprintf(fw, "  wireless-key s:%s\n", key.c_str());
1101       }
1102       else if (encryptionMode == ENC_WPA || encryptionMode == ENC_WPA2)
1103       {
1104          fprintf(fw, "  wpa-ssid %s\n", essId.c_str());
1105          fprintf(fw, "  wpa-psk %s\n", key.c_str());
1106          fprintf(fw, "  wpa-proto %s\n", encryptionMode == ENC_WPA ? "WPA" : "WPA2");
1107       }
1108    }
1109
1110    if (assignment != NETWORK_DISABLED)
1111       fprintf(fw, "auto %s\n\n", GetName().c_str());
1112 }
1113
1114