1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "NetworkLinux.h"
10 
11 #include "utils/StringUtils.h"
12 #include "utils/log.h"
13 
14 #include <errno.h>
15 #include <utility>
16 
17 #include <arpa/inet.h>
18 #include <net/if.h>
19 #include <net/if_arp.h>
20 #include <resolv.h>
21 #include <sys/ioctl.h>
22 
CNetworkInterfaceLinux(CNetworkPosix * network,std::string interfaceName,char interfaceMacAddrRaw[6])23 CNetworkInterfaceLinux::CNetworkInterfaceLinux(CNetworkPosix* network,
24                                                std::string interfaceName,
25                                                char interfaceMacAddrRaw[6])
26   : CNetworkInterfacePosix(network, std::move(interfaceName), interfaceMacAddrRaw)
27 {
28 }
29 
GetCurrentDefaultGateway() const30 std::string CNetworkInterfaceLinux::GetCurrentDefaultGateway() const
31 {
32   std::string result;
33 
34   FILE* fp = fopen("/proc/net/route", "r");
35   if (!fp)
36   {
37     // TBD: Error
38     return result;
39   }
40 
41   char* line = NULL;
42   char iface[16];
43   char dst[128];
44   char gateway[128];
45   size_t linel = 0;
46   int n;
47   int linenum = 0;
48   while (getdelim(&line, &linel, '\n', fp) > 0)
49   {
50     // skip first two lines
51     if (linenum++ < 1)
52       continue;
53 
54     // search where the word begins
55     n = sscanf(line, "%15s %127s %127s", iface, dst, gateway);
56 
57     if (n < 3)
58       continue;
59 
60     if (strcmp(iface, m_interfaceName.c_str()) == 0 && strcmp(dst, "00000000") == 0 &&
61         strcmp(gateway, "00000000") != 0)
62     {
63       unsigned char gatewayAddr[4];
64       int len = CNetworkBase::ParseHex(gateway, gatewayAddr);
65       if (len == 4)
66       {
67         struct in_addr in;
68         in.s_addr = (gatewayAddr[0] << 24) | (gatewayAddr[1] << 16) | (gatewayAddr[2] << 8) |
69                     (gatewayAddr[3]);
70         result = inet_ntoa(in);
71         break;
72       }
73     }
74   }
75   free(line);
76   fclose(fp);
77 
78   return result;
79 }
80 
GetHostMacAddress(unsigned long host_ip,std::string & mac) const81 bool CNetworkInterfaceLinux::GetHostMacAddress(unsigned long host_ip, std::string& mac) const
82 {
83   struct arpreq areq;
84   struct sockaddr_in* sin;
85 
86   memset(&areq, 0x0, sizeof(areq));
87 
88   sin = (struct sockaddr_in*)&areq.arp_pa;
89   sin->sin_family = AF_INET;
90   sin->sin_addr.s_addr = host_ip;
91 
92   sin = (struct sockaddr_in*)&areq.arp_ha;
93   sin->sin_family = ARPHRD_ETHER;
94 
95   strncpy(areq.arp_dev, m_interfaceName.c_str(), sizeof(areq.arp_dev));
96   areq.arp_dev[sizeof(areq.arp_dev) - 1] = '\0';
97 
98   int result = ioctl(m_network->GetSocket(), SIOCGARP, (caddr_t)&areq);
99 
100   if (result != 0)
101   {
102     //  CLog::Log(LOGERROR, "%s - GetHostMacAddress/ioctl failed with errno (%d)", __FUNCTION__, errno);
103     return false;
104   }
105 
106   struct sockaddr* res = &areq.arp_ha;
107   mac = StringUtils::Format("%02X:%02X:%02X:%02X:%02X:%02X", (uint8_t)res->sa_data[0],
108                             (uint8_t)res->sa_data[1], (uint8_t)res->sa_data[2],
109                             (uint8_t)res->sa_data[3], (uint8_t)res->sa_data[4],
110                             (uint8_t)res->sa_data[5]);
111 
112   for (int i = 0; i < 6; ++i)
113     if (res->sa_data[i])
114       return true;
115 
116   return false;
117 }
118 
CNetworkLinux()119 CNetworkLinux::CNetworkLinux() : CNetworkPosix()
120 {
121   queryInterfaceList();
122 }
123 
GetMacAddress(const std::string & interfaceName,char rawMac[6])124 void CNetworkLinux::GetMacAddress(const std::string& interfaceName, char rawMac[6])
125 {
126   memset(rawMac, 0, 6);
127 
128   struct ifreq ifr;
129   strcpy(ifr.ifr_name, interfaceName.c_str());
130   if (ioctl(GetSocket(), SIOCGIFHWADDR, &ifr) >= 0)
131   {
132     memcpy(rawMac, ifr.ifr_hwaddr.sa_data, 6);
133   }
134 }
135 
queryInterfaceList()136 void CNetworkLinux::queryInterfaceList()
137 {
138   char macAddrRaw[6];
139   m_interfaces.clear();
140 
141   FILE* fp = fopen("/proc/net/dev", "r");
142   if (!fp)
143   {
144     // TBD: Error
145     return;
146   }
147 
148   char* line = NULL;
149   size_t linel = 0;
150   int n;
151   char* p;
152   int linenum = 0;
153   while (getdelim(&line, &linel, '\n', fp) > 0)
154   {
155     // skip first two lines
156     if (linenum++ < 2)
157       continue;
158 
159     // search where the word begins
160     p = line;
161     while (isspace(*p))
162       ++p;
163 
164     // read word until :
165     n = strcspn(p, ": \t");
166     p[n] = 0;
167 
168     // save the result
169     std::string interfaceName = p;
170     GetMacAddress(interfaceName, macAddrRaw);
171 
172     // only add interfaces with non-zero mac addresses
173     if (macAddrRaw[0] || macAddrRaw[1] || macAddrRaw[2] || macAddrRaw[3] || macAddrRaw[4] ||
174         macAddrRaw[5])
175       m_interfaces.push_back(new CNetworkInterfaceLinux(this, interfaceName, macAddrRaw));
176   }
177   free(line);
178   fclose(fp);
179 }
180 
GetNameServers()181 std::vector<std::string> CNetworkLinux::GetNameServers()
182 {
183   std::vector<std::string> result;
184 
185   res_init();
186 
187   for (int i = 0; i < _res.nscount; i++)
188   {
189     std::string ns = inet_ntoa(_res.nsaddr_list[i].sin_addr);
190     result.push_back(ns);
191   }
192   return result;
193 }
194 
PingHost(unsigned long remote_ip,unsigned int timeout_ms)195 bool CNetworkLinux::PingHost(unsigned long remote_ip, unsigned int timeout_ms)
196 {
197   char cmd_line[64];
198 
199   struct in_addr host_ip;
200   host_ip.s_addr = remote_ip;
201 
202   sprintf(cmd_line, "ping -c 1 -w %d %s", timeout_ms / 1000 + (timeout_ms % 1000) != 0,
203           inet_ntoa(host_ip));
204 
205   int status = -1;
206   status = system(cmd_line);
207   int result = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
208 
209   // http://linux.about.com/od/commands/l/blcmdl8_ping.htm ;
210   // 0 reply
211   // 1 no reply
212   // else some error
213 
214   if (result < 0 || result > 1)
215     CLog::Log(LOGERROR, "Ping fail : status = %d, errno = %d : '%s'", status, errno, cmd_line);
216 
217   return result == 0;
218 }
219