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
This commit is contained in:
David Pursell 2016-01-21 08:40:59 -08:00
parent bbedd9523f
commit 706955ff0d
9 changed files with 253 additions and 148 deletions

View file

@ -35,6 +35,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@ -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()));
}

View file

@ -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<std::string> 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));
}

View file

@ -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);

View file

@ -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));

View file

@ -32,6 +32,7 @@
#endif
#include <android-base/file.h>
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
@ -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;
}

View file

@ -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 \

View file

@ -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 <string>
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

82
base/parsenetaddress.cpp Normal file
View file

@ -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 <algorithm>
#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<std::string> 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

View file

@ -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 <gtest/gtest.h>
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));
}