Merge "Recognize IPv6 addresses for "adb connect"."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a7a7e06..1727225 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -41,6 +41,7 @@
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_utils.h"
 #include "transport.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -896,28 +897,28 @@
 
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
-        char buffer[4096];
-        memset(buffer, 0, sizeof(buffer));
-        const char* serial = service + 11;
-        if (serial[0] == 0) {
+        const std::string address(service + 11);
+        if (address.empty()) {
             // disconnect from all TCP devices
             unregister_all_tcp_transports();
-        } else {
-            char hostbuf[100];
-            // assume port 5555 if no port is specified
-            if (!strchr(serial, ':')) {
-                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
-                serial = hostbuf;
-            }
-            atransport* t = find_transport(serial);
-            if (t) {
-                unregister_transport(t);
-            } else {
-                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
-            }
+            return SendOkay(reply_fd, "disconnected everything");
         }
 
-        return SendOkay(reply_fd, buffer);
+        std::string serial;
+        std::string host;
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        std::string error;
+        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                                  address.c_str(), error.c_str()));
+        }
+        atransport* t = find_transport(serial.c_str());
+        if (t == nullptr) {
+            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
+                                                                  serial.c_str()));
+        }
+        unregister_transport(t);
+        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
     // returns our value for ADB_SERVER_VERSION
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index e42d928..75e888d 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -277,8 +277,7 @@
     D("adb_query: %s\n", service.c_str());
     int fd = adb_connect(service, error);
     if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error->c_str());
-        return 0;
+        return false;
     }
 
     result->clear();
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 4b97a14..e2af045 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -25,7 +25,9 @@
 
 #include <algorithm>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb_trace.h"
 #include "sysdeps.h"
@@ -103,3 +105,56 @@
 
     DR("%s\n", line.c_str());
 }
+
+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;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index c816d00..e0aa1ba 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -28,4 +28,15 @@
 
 void 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);
+
 #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 052aea5..7aa610a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -50,3 +50,85 @@
   ASSERT_EQ(R"('abc(')", escape_arg("abc("));
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
+
+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));
+}
diff --git a/adb/services.cpp b/adb/services.cpp
index e3c17f9..b9c532a 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -47,6 +47,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "transport.h"
@@ -541,35 +542,27 @@
     D("wait_for_state is done\n");
 }
 
-static void connect_device(const std::string& host, std::string* response) {
-    if (host.empty()) {
-        *response = "empty host name";
+static void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
         return;
     }
 
-    std::vector<std::string> pieces = android::base::Split(host, ":");
-    const std::string& hostname = pieces[0];
-
+    std::string serial;
+    std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (pieces.size() > 1) {
-        if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
-            *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
-            return;
-        }
-    }
-
-    // This may look like we're putting 'host' back together,
-    // but we're actually inserting the default port if necessary.
-    std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
-
-    int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
-    if (fd < 0) {
-        *response = android::base::StringPrintf("unable to connect to %s:%d",
-                                                hostname.c_str(), port);
+    if (!parse_host_and_port(address, &serial, &host, &port, response)) {
         return;
     }
 
-    D("client: connected on remote on fd %d\n", fd);
+    int fd = socket_network_client_timeout(host.c_str(), port, SOCK_STREAM, 10);
+    if (fd == -1) {
+        *response = android::base::StringPrintf("unable to connect to %s: %s",
+                                                serial.c_str(), strerror(errno));
+        return;
+    }
+
+    D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);