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