From 706955ff0d159d28bb6eb06dc10178624c505f4e Mon Sep 17 00:00:00 2001 From: David Pursell Date: Thu, 21 Jan 2016 08:40:59 -0800 Subject: [PATCH] base: add network address parsing function from adb. This CL moves the network address parsing function from adb to libbase so that it can be used by fastboot as well as adb. libbase seemed like the right choice because: 1. It already has some parsing functions (parseint) 2. The net address parsing function uses the libbase string functions so we have a libbase dependency anyway. The parsing function has been modified slightly to make the canonical address optional, and debug logging on success has been removed. For adb the only functional difference is that parsing a network address will no longer print the result to the debug log, which seemed unnecessary. Bug: http://b/26236380 Change-Id: Ife6df02937225fc66de87884d3572d79c092c522 --- adb/adb.cpp | 3 +- adb/adb_utils.cpp | 53 -------- adb/adb_utils.h | 11 -- adb/adb_utils_test.cpp | 82 ------------- adb/services.cpp | 3 +- base/Android.mk | 2 + base/include/android-base/parsenetaddress.h | 38 ++++++ base/parsenetaddress.cpp | 82 +++++++++++++ base/parsenetaddress_test.cpp | 127 ++++++++++++++++++++ 9 files changed, 253 insertions(+), 148 deletions(-) create mode 100644 base/include/android-base/parsenetaddress.h create mode 100644 base/parsenetaddress.cpp create mode 100644 base/parsenetaddress_test.cpp diff --git a/adb/adb.cpp b/adb/adb.cpp index 484e56165..30056528f 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -1037,7 +1038,7 @@ int handle_host_request(const char* service, TransportType type, std::string host; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; std::string error; - if (!parse_host_and_port(address, &serial, &host, &port, &error)) { + if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) { return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s", address.c_str(), error.c_str())); } diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp index b132118ac..474d1b403 100644 --- a/adb/adb_utils.cpp +++ b/adb/adb_utils.cpp @@ -208,59 +208,6 @@ std::string dump_hex(const void* data, size_t byte_count) { return line; } -bool parse_host_and_port(const std::string& address, - std::string* canonical_address, - std::string* host, int* port, - std::string* error) { - host->clear(); - - bool ipv6 = true; - bool saw_port = false; - size_t colons = std::count(address.begin(), address.end(), ':'); - size_t dots = std::count(address.begin(), address.end(), '.'); - std::string port_str; - if (address[0] == '[') { - // [::1]:123 - if (address.rfind("]:") == std::string::npos) { - *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str()); - return false; - } - *host = address.substr(1, (address.find("]:") - 1)); - port_str = address.substr(address.rfind("]:") + 2); - saw_port = true; - } else if (dots == 0 && colons >= 2 && colons <= 7) { - // ::1 - *host = address; - } else if (colons <= 1) { - // 1.2.3.4 or some.accidental.domain.com - ipv6 = false; - std::vector pieces = android::base::Split(address, ":"); - *host = pieces[0]; - if (pieces.size() > 1) { - port_str = pieces[1]; - saw_port = true; - } - } - - if (host->empty()) { - *error = android::base::StringPrintf("no host in '%s'", address.c_str()); - return false; - } - - if (saw_port) { - if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) { - *error = android::base::StringPrintf("bad port number '%s' in '%s'", - port_str.c_str(), address.c_str()); - return false; - } - } - - *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port); - LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port - << " (" << *canonical_address << ")"; - return true; -} - std::string perror_str(const char* msg) { return android::base::StringPrintf("%s: %s", msg, strerror(errno)); } diff --git a/adb/adb_utils.h b/adb/adb_utils.h index 388d7dde3..f1149b3d7 100644 --- a/adb/adb_utils.h +++ b/adb/adb_utils.h @@ -35,17 +35,6 @@ std::string escape_arg(const std::string& s); std::string dump_hex(const void* ptr, size_t byte_count); -// Parses 'address' into 'host' and 'port'. -// If no port is given, takes the default from *port. -// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate. -// Note that no real checking is done that 'host' or 'port' is valid; that's -// left to getaddrinfo(3). -// Returns false on failure and sets *error to an appropriate message. -bool parse_host_and_port(const std::string& address, - std::string* canonical_address, - std::string* host, int* port, - std::string* error); - std::string perror_str(const char* msg); bool set_file_block_mode(int fd, bool block); diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp index 4508bca2b..794dce63a 100644 --- a/adb/adb_utils_test.cpp +++ b/adb/adb_utils_test.cpp @@ -111,88 +111,6 @@ TEST(adb_utils, adb_dirname) { EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/")); } -TEST(adb_utils, parse_host_and_port) { - std::string canonical_address; - std::string host; - int port; - std::string error; - - // Name, default port. - port = 123; - ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error)); - ASSERT_EQ("www.google.com:123", canonical_address); - ASSERT_EQ("www.google.com", host); - ASSERT_EQ(123, port); - - // Name, explicit port. - ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error)); - ASSERT_EQ("www.google.com:666", canonical_address); - ASSERT_EQ("www.google.com", host); - ASSERT_EQ(666, port); - - // IPv4, default port. - port = 123; - ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error)); - ASSERT_EQ("1.2.3.4:123", canonical_address); - ASSERT_EQ("1.2.3.4", host); - ASSERT_EQ(123, port); - - // IPv4, explicit port. - ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error)); - ASSERT_EQ("1.2.3.4:666", canonical_address); - ASSERT_EQ("1.2.3.4", host); - ASSERT_EQ(666, port); - - // Simple IPv6, default port. - port = 123; - ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error)); - ASSERT_EQ("[::1]:123", canonical_address); - ASSERT_EQ("::1", host); - ASSERT_EQ(123, port); - - // Simple IPv6, explicit port. - ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error)); - ASSERT_EQ("[::1]:666", canonical_address); - ASSERT_EQ("::1", host); - ASSERT_EQ(666, port); - - // Hairy IPv6, default port. - port = 123; - ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error)); - ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address); - ASSERT_EQ("fe80::200:5aee:feaa:20a2", host); - ASSERT_EQ(123, port); - - // Simple IPv6, explicit port. - ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error)); - ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address); - ASSERT_EQ("fe80::200:5aee:feaa:20a2", host); - ASSERT_EQ(666, port); - - // Invalid IPv4. - EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error)); - - // Invalid IPv6. - EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error)); - - // Invalid ports. - EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error)); - EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error)); -} - void test_mkdirs(const std::string basepath) { EXPECT_TRUE(mkdirs(basepath)); EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600)); diff --git a/adb/services.cpp b/adb/services.cpp index cd33e7b7d..9cbf78794 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -32,6 +32,7 @@ #endif #include +#include #include #include #include @@ -396,7 +397,7 @@ static void connect_device(const std::string& address, std::string* response) { std::string serial; std::string host; int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; - if (!parse_host_and_port(address, &serial, &host, &port, response)) { + if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) { return; } diff --git a/base/Android.mk b/base/Android.mk index cba70d4c9..18f86866d 100644 --- a/base/Android.mk +++ b/base/Android.mk @@ -19,6 +19,7 @@ LOCAL_PATH := $(call my-dir) libbase_src_files := \ file.cpp \ logging.cpp \ + parsenetaddress.cpp \ stringprintf.cpp \ strings.cpp \ test_utils.cpp \ @@ -30,6 +31,7 @@ libbase_test_src_files := \ file_test.cpp \ logging_test.cpp \ parseint_test.cpp \ + parsenetaddress_test.cpp \ stringprintf_test.cpp \ strings_test.cpp \ test_main.cpp \ diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h new file mode 100644 index 000000000..2de5ac93d --- /dev/null +++ b/base/include/android-base/parsenetaddress.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_PARSENETADDRESS_H +#define BASE_PARSENETADDRESS_H + +#include + +namespace android { +namespace base { + +// Parses |address| into |host| and |port|. +// +// If |address| doesn't contain a port number, the default value is taken from +// |port|. If |canonical_address| is non-null it will be set to "host:port" or +// "[host]:port" as appropriate. +// +// On failure, returns false and fills |error|. +bool ParseNetAddress(const std::string& address, std::string* host, int* port, + std::string* canonical_address, std::string* error); + +} // namespace base +} // namespace android + +#endif // BASE_PARSENETADDRESS_H diff --git a/base/parsenetaddress.cpp b/base/parsenetaddress.cpp new file mode 100644 index 000000000..dd80f6da2 --- /dev/null +++ b/base/parsenetaddress.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/parsenetaddress.h" + +#include + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +namespace android { +namespace base { + +bool ParseNetAddress(const std::string& address, std::string* host, int* port, + std::string* canonical_address, std::string* error) { + host->clear(); + + bool ipv6 = true; + bool saw_port = false; + size_t colons = std::count(address.begin(), address.end(), ':'); + size_t dots = std::count(address.begin(), address.end(), '.'); + std::string port_str; + if (address[0] == '[') { + // [::1]:123 + if (address.rfind("]:") == std::string::npos) { + *error = StringPrintf("bad IPv6 address '%s'", address.c_str()); + return false; + } + *host = address.substr(1, (address.find("]:") - 1)); + port_str = address.substr(address.rfind("]:") + 2); + saw_port = true; + } else if (dots == 0 && colons >= 2 && colons <= 7) { + // ::1 + *host = address; + } else if (colons <= 1) { + // 1.2.3.4 or some.accidental.domain.com + ipv6 = false; + std::vector pieces = Split(address, ":"); + *host = pieces[0]; + if (pieces.size() > 1) { + port_str = pieces[1]; + saw_port = true; + } + } + + if (host->empty()) { + *error = StringPrintf("no host in '%s'", address.c_str()); + return false; + } + + if (saw_port) { + if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || + *port > 65535) { + *error = StringPrintf("bad port number '%s' in '%s'", port_str.c_str(), + address.c_str()); + return false; + } + } + + if (canonical_address != nullptr) { + *canonical_address = + StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port); + } + + return true; +} + +} // namespace base +} // namespace android diff --git a/base/parsenetaddress_test.cpp b/base/parsenetaddress_test.cpp new file mode 100644 index 000000000..a3bfac860 --- /dev/null +++ b/base/parsenetaddress_test.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/parsenetaddress.h" + +#include + +using android::base::ParseNetAddress; + +TEST(ParseNetAddressTest, TestUrl) { + std::string canonical, host, error; + int port = 123; + + EXPECT_TRUE( + ParseNetAddress("www.google.com", &host, &port, &canonical, &error)); + EXPECT_EQ("www.google.com:123", canonical); + EXPECT_EQ("www.google.com", host); + EXPECT_EQ(123, port); + + EXPECT_TRUE( + ParseNetAddress("www.google.com:666", &host, &port, &canonical, &error)); + EXPECT_EQ("www.google.com:666", canonical); + EXPECT_EQ("www.google.com", host); + EXPECT_EQ(666, port); +} + +TEST(ParseNetAddressTest, TestIpv4) { + std::string canonical, host, error; + int port = 123; + + EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, &canonical, &error)); + EXPECT_EQ("1.2.3.4:123", canonical); + EXPECT_EQ("1.2.3.4", host); + EXPECT_EQ(123, port); + + EXPECT_TRUE(ParseNetAddress("1.2.3.4:666", &host, &port, &canonical, &error)); + EXPECT_EQ("1.2.3.4:666", canonical); + EXPECT_EQ("1.2.3.4", host); + EXPECT_EQ(666, port); +} + +TEST(ParseNetAddressTest, TestIpv6) { + std::string canonical, host, error; + int port = 123; + + EXPECT_TRUE(ParseNetAddress("::1", &host, &port, &canonical, &error)); + EXPECT_EQ("[::1]:123", canonical); + EXPECT_EQ("::1", host); + EXPECT_EQ(123, port); + + EXPECT_TRUE(ParseNetAddress("fe80::200:5aee:feaa:20a2", &host, &port, + &canonical, &error)); + EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical); + EXPECT_EQ("fe80::200:5aee:feaa:20a2", host); + EXPECT_EQ(123, port); + + EXPECT_TRUE(ParseNetAddress("[::1]:666", &host, &port, &canonical, &error)); + EXPECT_EQ("[::1]:666", canonical); + EXPECT_EQ("::1", host); + EXPECT_EQ(666, port); + + EXPECT_TRUE(ParseNetAddress("[fe80::200:5aee:feaa:20a2]:666", &host, &port, + &canonical, &error)); + EXPECT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical); + EXPECT_EQ("fe80::200:5aee:feaa:20a2", host); + EXPECT_EQ(666, port); +} + +TEST(ParseNetAddressTest, TestInvalidAddress) { + std::string canonical, host; + int port; + + std::string failure_cases[] = { + // Invalid IPv4. + "1.2.3.4:", + "1.2.3.4::", + ":123", + + // Invalid IPv6. + ":1", + "::::::::1", + "[::1", + "[::1]", + "[::1]:", + "[::1]::", + + // Invalid port. + "1.2.3.4:-1", + "1.2.3.4:0", + "1.2.3.4:65536" + "1.2.3.4:hello", + "[::1]:-1", + "[::1]:0", + "[::1]:65536", + "[::1]:hello", + }; + + for (const auto& address : failure_cases) { + // Failure should give some non-empty error string. + std::string error; + EXPECT_FALSE(ParseNetAddress(address, &host, &port, &canonical, &error)); + EXPECT_NE("", error); + } +} + +// Null canonical address argument. +TEST(ParseNetAddressTest, TestNullCanonicalAddress) { + std::string host, error; + int port = 42; + + EXPECT_TRUE(ParseNetAddress("www.google.com", &host, &port, nullptr, &error)); + EXPECT_TRUE(ParseNetAddress("1.2.3.4", &host, &port, nullptr, &error)); + EXPECT_TRUE(ParseNetAddress("::1", &host, &port, nullptr, &error)); +}