fastboot: Fix IPv6 connect and -s host parsing

During fastboot connect / disconnect introduction, we
completely broke the IPv6 support (it was considering
all IPv6 addresses as a USB serial).

Makeing sure this problem isn't reproducible anymore
alongside with fixing EXCPECT causing process crash
and improve network serial error detection.

Bug: 271152365
Change-Id: Ic52aa5fff1948a64ac3d2672f3cf4d2b022e5cea
Signed-off-by: Dmitrii Merkurev <dimorinny@google.com>
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 799c9f9..602ba01 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -342,30 +342,49 @@
     int port;
 };
 
-static Result<NetworkSerial> ParseNetworkSerial(const std::string& serial) {
-    const auto serial_parsed = android::base::Tokenize(serial, ":");
-    const auto parsed_segments_count = serial_parsed.size();
-    if (parsed_segments_count != 2 && parsed_segments_count != 3) {
-        return Error() << "invalid network address: " << serial << ". Expected format:\n"
-                       << "<protocol>:<address>:<port> (tcp:localhost:5554)";
-    }
+class ParseNetworkAddressError {
+  public:
+    enum Type { WRONG_PREFIX = 1, WRONG_ADDRESS = 2 };
 
+    ParseNetworkAddressError(Type&& type) : type_(std::forward<Type>(type)) {}
+
+    Type value() const { return type_; }
+    operator Type() const { return value(); }
+    std::string print() const { return ""; }
+
+  private:
+    Type type_;
+};
+
+static Result<NetworkSerial, ParseNetworkAddressError> ParseNetworkSerial(
+        const std::string& serial) {
     Socket::Protocol protocol;
-    if (serial_parsed[0] == "tcp") {
+    const char* net_address = nullptr;
+    int port = 0;
+
+    if (android::base::StartsWith(serial, "tcp:")) {
         protocol = Socket::Protocol::kTcp;
-    } else if (serial_parsed[0] == "udp") {
+        net_address = serial.c_str() + strlen("tcp:");
+        port = tcp::kDefaultPort;
+    } else if (android::base::StartsWith(serial, "udp:")) {
         protocol = Socket::Protocol::kUdp;
+        net_address = serial.c_str() + strlen("udp:");
+        port = udp::kDefaultPort;
     } else {
-        return Error() << "invalid network address: " << serial << ". Expected format:\n"
-                       << "<protocol>:<address>:<port> (tcp:localhost:5554)";
+        return Error<ParseNetworkAddressError>(ParseNetworkAddressError::Type::WRONG_PREFIX)
+               << "protocol prefix ('tcp:' or 'udp:') is missed: " << serial << ". "
+               << "Expected address format:\n"
+               << "<protocol>:<address>:<port> (tcp:localhost:5554)";
     }
 
-    int port = 5554;
-    if (parsed_segments_count == 3) {
-        android::base::ParseInt(serial_parsed[2], &port, 5554);
+    std::string error;
+    std::string host;
+    if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+        return Error<ParseNetworkAddressError>(ParseNetworkAddressError::Type::WRONG_ADDRESS)
+               << "invalid network address '" << net_address << "': " << error;
     }
 
-    return NetworkSerial{protocol, serial_parsed[1], port};
+    return NetworkSerial{protocol, host, port};
 }
 
 // Opens a new Transport connected to the particular device.
@@ -380,7 +399,8 @@
 // object, and the caller should not attempt to delete the returned Transport.
 static Transport* open_device(const char* local_serial, bool wait_for_device = true,
                               bool announce = true) {
-    const Result<NetworkSerial> network_serial = ParseNetworkSerial(local_serial);
+    const Result<NetworkSerial, ParseNetworkAddressError> network_serial =
+            ParseNetworkSerial(local_serial);
 
     Transport* transport = nullptr;
     while (true) {
@@ -397,8 +417,12 @@
             if (transport == nullptr && announce) {
                 LOG(ERROR) << "error: " << error;
             }
-        } else {
+        } else if (network_serial.error().code() == ParseNetworkAddressError::Type::WRONG_PREFIX) {
+            // WRONG_PREFIX is special because it happens when user wants to communicate with USB
+            // device
             transport = usb_open(match_fastboot(local_serial));
+        } else {
+            Expect(network_serial);
         }
 
         if (transport != nullptr) {
@@ -413,7 +437,7 @@
             announce = false;
             LOG(ERROR) << "< waiting for " << local_serial << ">";
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
 }
 
@@ -476,7 +500,7 @@
             announce = false;
             LOG(ERROR) << "< waiting for any device >";
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
 }
 
@@ -487,7 +511,7 @@
     }
 
     const char* local_serial = *argv;
-    EXPECT(ParseNetworkSerial(local_serial));
+    Expect(ParseNetworkSerial(local_serial));
 
     const Transport* transport = open_device(local_serial, false);
     if (transport == nullptr) {
@@ -506,7 +530,7 @@
 }
 
 static int Disconnect(const char* local_serial) {
-    EXPECT(ParseNetworkSerial(local_serial));
+    Expect(ParseNetworkSerial(local_serial));
 
     ConnectedDevicesStorage storage;
     {
@@ -1555,7 +1579,7 @@
     delete old_transport;
 
     // Give the current connection time to close.
-    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+    std::this_thread::sleep_for(std::chrono::seconds(1));
 
     fb->set_transport(open_device());