| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/socket_info_reader.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <string> |
| #include <vector> |
| |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/string_util.h> |
| |
| #include "shill/file_reader.h" |
| #include "shill/logging.h" |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kLink; |
| static std::string ObjectID(const SocketInfoReader* s) { |
| return "(socket_info_reader)"; |
| } |
| } // namespace Logging |
| |
| namespace { |
| |
| const char kTcpv4SocketInfoFilePath[] = "/proc/net/tcp"; |
| const char kTcpv6SocketInfoFilePath[] = "/proc/net/tcp6"; |
| |
| } // namespace |
| |
| SocketInfoReader::SocketInfoReader() = default; |
| |
| SocketInfoReader::~SocketInfoReader() = default; |
| |
| base::FilePath SocketInfoReader::GetTcpv4SocketInfoFilePath() const { |
| return base::FilePath(kTcpv4SocketInfoFilePath); |
| } |
| |
| base::FilePath SocketInfoReader::GetTcpv6SocketInfoFilePath() const { |
| return base::FilePath(kTcpv6SocketInfoFilePath); |
| } |
| |
| bool SocketInfoReader::LoadTcpSocketInfo(std::vector<SocketInfo>* info_list) { |
| info_list->clear(); |
| bool v4_loaded = AppendSocketInfo(GetTcpv4SocketInfoFilePath(), info_list); |
| bool v6_loaded = AppendSocketInfo(GetTcpv6SocketInfoFilePath(), info_list); |
| // Return true if we can load either /proc/net/tcp or /proc/net/tcp6 |
| // successfully. |
| return v4_loaded || v6_loaded; |
| } |
| |
| bool SocketInfoReader::AppendSocketInfo(const base::FilePath& info_file_path, |
| std::vector<SocketInfo>* info_list) { |
| FileReader file_reader; |
| if (!file_reader.Open(info_file_path)) { |
| SLOG(this, 2) << __func__ << ": Failed to open '" << info_file_path.value() |
| << "'."; |
| return false; |
| } |
| |
| std::string line; |
| while (file_reader.ReadLine(&line)) { |
| SocketInfo socket_info; |
| if (ParseSocketInfo(line, &socket_info)) |
| info_list->push_back(socket_info); |
| } |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseSocketInfo(const std::string& input, |
| SocketInfo* socket_info) { |
| const auto tokens = |
| base::SplitString(input, base::kWhitespaceASCII, base::KEEP_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| if (tokens.size() < 10) { |
| return false; |
| } |
| |
| SocketInfo info; |
| if (!ParseIPAddressAndPort(tokens[1], &info.local_ip_address, |
| &info.local_port)) { |
| return false; |
| } |
| |
| if (!ParseIPAddressAndPort(tokens[2], &info.remote_ip_address, |
| &info.remote_port)) { |
| return false; |
| } |
| |
| if (!ParseConnectionState(tokens[3], &info.connection_state)) { |
| return false; |
| } |
| |
| if (!ParseTransimitAndReceiveQueueValues( |
| tokens[4], &info.transmit_queue_value, &info.receive_queue_value)) { |
| return false; |
| } |
| |
| if (!ParseTimerState(tokens[5], &info.timer_state)) { |
| return false; |
| } |
| |
| *socket_info = info; |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseIPAddressAndPort(const std::string& input, |
| IPAddress* ip_address, |
| uint16_t* port) { |
| const auto tokens = base::SplitString(input, ":", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_ALL); |
| if (tokens.size() != 2 || !ParseIPAddress(tokens[0], ip_address) || |
| !ParsePort(tokens[1], port)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseIPAddress(const std::string& input, |
| IPAddress* ip_address) { |
| ByteString byte_string = ByteString::CreateFromHexString(input); |
| if (byte_string.IsEmpty()) |
| return false; |
| |
| IPAddress::Family family; |
| if (byte_string.GetLength() == |
| IPAddress::GetAddressLength(IPAddress::kFamilyIPv4)) { |
| family = IPAddress::kFamilyIPv4; |
| } else if (byte_string.GetLength() == |
| IPAddress::GetAddressLength(IPAddress::kFamilyIPv6)) { |
| family = IPAddress::kFamilyIPv6; |
| } else { |
| return false; |
| } |
| |
| // Linux kernel prints out IP addresses in network order via |
| // /proc/net/tcp{,6}. |
| byte_string.ConvertFromNetToCPUUInt32Array(); |
| |
| *ip_address = IPAddress(family, byte_string); |
| return true; |
| } |
| |
| bool SocketInfoReader::ParsePort(const std::string& input, uint16_t* port) { |
| int result = 0; |
| |
| if (input.size() != 4 || !base::HexStringToInt(input, &result) || |
| result < 0 || result > std::numeric_limits<uint16_t>::max()) { |
| return false; |
| } |
| |
| *port = result; |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseTransimitAndReceiveQueueValues( |
| const std::string& input, |
| uint64_t* transmit_queue_value, |
| uint64_t* receive_queue_value) { |
| int64_t signed_transmit_queue_value = 0, signed_receive_queue_value = 0; |
| |
| const auto tokens = base::SplitString(input, ":", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_ALL); |
| if (tokens.size() != 2 || |
| !base::HexStringToInt64(tokens[0], &signed_transmit_queue_value) || |
| !base::HexStringToInt64(tokens[1], &signed_receive_queue_value)) { |
| return false; |
| } |
| |
| *transmit_queue_value = static_cast<uint64_t>(signed_transmit_queue_value); |
| *receive_queue_value = static_cast<uint64_t>(signed_receive_queue_value); |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseConnectionState( |
| const std::string& input, SocketInfo::ConnectionState* connection_state) { |
| int result = 0; |
| |
| if (input.size() != 2 || !base::HexStringToInt(input, &result)) { |
| return false; |
| } |
| |
| if (result > 0 && result < SocketInfo::kConnectionStateMax) { |
| *connection_state = static_cast<SocketInfo::ConnectionState>(result); |
| } else { |
| *connection_state = SocketInfo::kConnectionStateUnknown; |
| } |
| return true; |
| } |
| |
| bool SocketInfoReader::ParseTimerState(const std::string& input, |
| SocketInfo::TimerState* timer_state) { |
| int result = 0; |
| |
| const auto tokens = base::SplitString(input, ":", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_ALL); |
| if (tokens.size() != 2 || tokens[0].size() != 2 || |
| !base::HexStringToInt(tokens[0], &result)) { |
| return false; |
| } |
| |
| if (result < SocketInfo::kTimerStateMax) { |
| *timer_state = static_cast<SocketInfo::TimerState>(result); |
| } else { |
| *timer_state = SocketInfo::kTimerStateUnknown; |
| } |
| return true; |
| } |
| |
| } // namespace shill |