diff options
-rw-r--r-- | chrome/browser/geolocation/empty_device_data_provider.cc | 2 | ||||
-rw-r--r-- | chrome/browser/geolocation/gateway_data_provider_linux.cc | 226 | ||||
-rw-r--r-- | chrome/browser/geolocation/gateway_data_provider_linux.h | 22 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 2 |
5 files changed, 252 insertions, 2 deletions
diff --git a/chrome/browser/geolocation/empty_device_data_provider.cc b/chrome/browser/geolocation/empty_device_data_provider.cc index b11dd79..2b95c88 100644 --- a/chrome/browser/geolocation/empty_device_data_provider.cc +++ b/chrome/browser/geolocation/empty_device_data_provider.cc @@ -21,7 +21,7 @@ WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { #endif // Only define for platforms that lack a real gateway data provider. -#if !defined(OS_WIN) +#if !defined(OS_LINUX) && !defined(OS_WIN) // static template<> GatewayDataProviderImplBase* GatewayDataProvider::DefaultFactoryFunction() { diff --git a/chrome/browser/geolocation/gateway_data_provider_linux.cc b/chrome/browser/geolocation/gateway_data_provider_linux.cc new file mode 100644 index 0000000..f1779cb --- /dev/null +++ b/chrome/browser/geolocation/gateway_data_provider_linux.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Provides MAC addresses of connected routers by using the /proc/net/ +// directory which contains files with network information. +// This directory is used in most Linux based operating systems. + +#include "chrome/browser/geolocation/gateway_data_provider_linux.h" + +#include <algorithm> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <set> +#include <string> +#include <sys/ioctl.h> +#include <vector> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/string_number_conversions.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/geolocation/empty_device_data_provider.h" +#include "chrome/browser/geolocation/gateway_data_provider_common.h" +#include "chrome/common/chrome_switches.h" + +namespace { +const unsigned int kMaxArpIterations = 30; +const unsigned int kMaxRouteIterations = 20; +const char kNoGateway[] = "00000000"; +const char kArpFilePath[] = "/proc/net/arp"; +const char kRouteFilePath[] = "/proc/net/route"; + +// Converts an IP address held in a little endian paired hex format to period +// separated decimal format. For example 0101A8C0 becomes 192.168.1.1 +std::string HexToIpv4Format(std::string& hex_address) { + if (hex_address.size() != 8) + return ""; + std::vector<uint8> bytes; + base::HexStringToBytes(hex_address, &bytes); + if (bytes.size() != 4) + return ""; + struct in_addr in; + uint32 ip_native_endian; + memcpy(&ip_native_endian, &bytes[0], 4); + in.s_addr = htonl(ip_native_endian); + return inet_ntoa(in); +} + +// TODO(joth): Cache the sets of gateways and MAC addresses to avoid reading +// through the arp table when the routing table hasn't changed. +class LinuxGatewayApi : public GatewayDataProviderCommon::GatewayApiInterface { + public: + LinuxGatewayApi() {} + virtual ~LinuxGatewayApi() {} + + static LinuxGatewayApi* Create() { + return new LinuxGatewayApi(); + } + + // GatewayApiInterface + virtual bool GetRouterData(GatewayData::RouterDataSet* data); + + private: + DISALLOW_COPY_AND_ASSIGN(LinuxGatewayApi); +}; + +// Gets a set of gateways using data from /proc/net/route +// Returns false if we cannot read the route file or we cannot classify the +// type of network adapter. +// Routing data is held in the following format +// Note: "|" represents a delimeter. +// Iface|Destination|Gateway|Flags|RefCnt|Use|Metric|Mask|MTU|Window|IRTT +// The delimiter for this table is "\t". +// ioctl calls are used to verify the type of network adapter, using interface +// names from the route table. +bool GetGateways(std::set<std::string>* gateways) { + FilePath route_file_path(kRouteFilePath); + std::string route_file; + if (!file_util::ReadFileToString(route_file_path, &route_file)) { + // Unable to read file. + return false; + } + StringTokenizer tokenized_route_file(route_file, "\n"); + std::vector<std::string> route_data; + // Remove column labels. + tokenized_route_file.GetNext(); + + // Get rows of data. + while (tokenized_route_file.GetNext() && + route_data.size() < kMaxRouteIterations) + route_data.push_back(tokenized_route_file.token()); + + if (route_data.empty()) { + // Having no data is not an error case. + return true; + } + + int sock; + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + return false; + struct ifreq network_device; + + for (size_t j = 0; j < route_data.size(); ++j) { + // Get gateway address. + std::string gateway; + StringTokenizer tokenized_route_data(route_data[j], "\t"); + // Check if device is an Ethernet device. + if (!tokenized_route_data.GetNext()) // Get Iface. + continue; + // Ensure space for the null term too. + if (tokenized_route_data.token().size() >= + arraysize(network_device.ifr_name)) + continue; + memset(&network_device, 0, sizeof(network_device)); + base::strlcpy(network_device.ifr_name, + tokenized_route_data.token().c_str(), + arraysize(network_device.ifr_name)); + if (ioctl(sock, SIOCGIFHWADDR, + reinterpret_cast<char*>(&network_device)) < 0) { + // Could not get device type. + continue; + } + if (network_device.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + // Device is not an Ethernet device. + continue; + } + if (!tokenized_route_data.GetNext()) // Skip Destination. + continue; + if (!tokenized_route_data.GetNext()) // Get Gateway. + continue; + gateway = tokenized_route_data.token(); + if (gateway != kNoGateway) { + // TODO(joth): Currently only works for gateways using IPv4. + std::string gateway_int_format = HexToIpv4Format(gateway); + if (!gateway_int_format.empty()) + gateways->insert(gateway_int_format); + } + } + + close(sock); + return true; +} + +// Gets a RouterDataSet containing MAC addresses related to any connected +// routers. Returns false if we cannot read the arp file. +// The /proc/net/arp file contains arp data in the following format +// Note: "|" represents a delimeter. +// IP address|HW type|Flags|HW address|Mask|Device +// The delimiter for this table is " ". +bool GetMacAddresses(GatewayData::RouterDataSet* data, + std::set<std::string>* gateways) { + // Find MAC addresses of routers + FilePath arp_file_path(kArpFilePath); + std::string arp_file_out; + if (!file_util::ReadFileToString(arp_file_path, &arp_file_out)) { + // Unable to read file. + return false; + } + StringTokenizer tokenized_arp_file(arp_file_out, "\n"); + std::vector<std::string> arp_data; + // Remove column labels. + tokenized_arp_file.GetNext(); + + // Get rows of data. + while (tokenized_arp_file.GetNext() && arp_data.size() < kMaxArpIterations) + arp_data.push_back(tokenized_arp_file.token()); + + for (size_t k = 0; k < arp_data.size(); k++) { + std::string ip_address; + StringTokenizer tokenized_arp_data(arp_data[k], " "); + if (!tokenized_arp_data.GetNext()) // Get IP address. + continue; + ip_address = tokenized_arp_data.token(); + if (gateways->find(ip_address) == gateways->end()) + continue; + if (!tokenized_arp_data.GetNext()) // Skip HW type. + continue; + if (!tokenized_arp_data.GetNext()) // Skip flags. + continue; + if (!tokenized_arp_data.GetNext()) // Get HW address. + continue; + RouterData router; + std::string mac_add; + mac_add = tokenized_arp_data.token(); + std::replace(mac_add.begin(), mac_add.end(), ':', '-'); + router.mac_address = UTF8ToUTF16(mac_add); + data->insert(router); + } + + return true; +} + +bool LinuxGatewayApi::GetRouterData(GatewayData::RouterDataSet* data) { + std::set<std::string> gateways; + if (!GetGateways(&gateways)) + return false; + if (gateways.empty()) + return true; + return GetMacAddresses(data, &gateways); +} +} // namespace + +// static +template<> +GatewayDataProviderImplBase* GatewayDataProvider::DefaultFactoryFunction() { + if (!CommandLine::ForCurrentProcess() + ->HasSwitch(switches::kExperimentalLocationFeatures)) + return new EmptyDeviceDataProvider<GatewayData>(); + return new GatewayDataProviderLinux(); +} + +GatewayDataProviderLinux::GatewayDataProviderLinux() { +} + +GatewayDataProviderLinux::~GatewayDataProviderLinux() { +} + +GatewayDataProviderCommon::GatewayApiInterface* + GatewayDataProviderLinux::NewGatewayApi() { + return LinuxGatewayApi::Create(); +} diff --git a/chrome/browser/geolocation/gateway_data_provider_linux.h b/chrome/browser/geolocation/gateway_data_provider_linux.h new file mode 100644 index 0000000..5d01d0b --- /dev/null +++ b/chrome/browser/geolocation/gateway_data_provider_linux.h @@ -0,0 +1,22 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_LINUX_H_ +#define CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_LINUX_H_ +#pragma once +#include "chrome/browser/geolocation/gateway_data_provider_common.h" + +class GatewayDataProviderLinux : public GatewayDataProviderCommon { + public: + GatewayDataProviderLinux(); + + private: + virtual ~GatewayDataProviderLinux(); + + // GatewayDataProviderCommon + virtual GatewayApiInterface* NewGatewayApi(); + DISALLOW_COPY_AND_ASSIGN(GatewayDataProviderLinux); +}; + +#endif // CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_LINUX_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 6bbebcf..82d8823 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1644,6 +1644,8 @@ 'browser/geolocation/empty_device_data_provider.h', 'browser/geolocation/gateway_data_provider_common.cc', 'browser/geolocation/gateway_data_provider_common.h', + 'browser/geolocation/gateway_data_provider_linux.cc', + 'browser/geolocation/gateway_data_provider_linux.h', 'browser/geolocation/gateway_data_provider_win.cc', 'browser/geolocation/gateway_data_provider_win.h', 'browser/geolocation/geolocation_content_settings_map.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 60dfd87..f2ba206 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -567,7 +567,7 @@ const char kEnableXSSAuditor[] = "enable-xss-auditor"; // Enables experimental features for the geolocation API. // Current features: // - CoreLocation support for Mac OS X 10.6 -// - Gateway location for Windows +// - Gateway location for Linux and Windows // - Location platform support for Windows 7 const char kExperimentalLocationFeatures[] = "experimental-location-features"; |