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