Remove sscanf() in TetherController.

Test: netd_unit_test, netd_integration_test, libnetd_resolv_test
Change-Id: Ibff2d6cc5dbf4fb09fcd1862be1b71be3b1d9f8e
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 7d7be58..20e7cee 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -30,6 +30,7 @@
 
 #include <array>
 #include <cstdlib>
+#include <regex>
 #include <string>
 #include <vector>
 
@@ -117,8 +118,6 @@
 
 auto TetherController::iptablesRestoreFunction = execIptablesRestoreWithOutput;
 
-const int MAX_IPT_OUTPUT_LINE_LEN = 256;
-
 const std::string GET_TETHER_STATS_COMMAND = StringPrintf(
     "*filter\n"
     "-nvx -L %s\n"
@@ -788,66 +787,75 @@
 int TetherController::addForwardChainStats(TetherStatsList& statsList,
                                            const std::string& statsOutput,
                                            std::string &extraProcessingInfo) {
-    int res;
-    std::string statsLine;
-    char iface0[MAX_IPT_OUTPUT_LINE_LEN];
-    char iface1[MAX_IPT_OUTPUT_LINE_LEN];
-    char rest[MAX_IPT_OUTPUT_LINE_LEN];
-
+    enum IndexOfIptChain {
+        ORIG_LINE,
+        PACKET_COUNTS,
+        BYTE_COUNTS,
+        HYPHEN,
+        IFACE0_NAME,
+        IFACE1_NAME,
+        SOURCE,
+        DESTINATION
+    };
     TetherStats stats;
     const TetherStats empty;
-    const char *buffPtr;
-    int64_t packets, bytes;
 
-    std::stringstream stream(statsOutput);
+    static const std::string NUM = "(\\d+)";
+    static const std::string IFACE = "([^\\s]+)";
+    static const std::string DST = "(0.0.0.0/0|::/0)";
+    static const std::string COUNTERS = "\\s*" + NUM + "\\s+" + NUM +
+                                        " RETURN     all(  --  |      )" + IFACE + "\\s+" + IFACE +
+                                        "\\s+" + DST + "\\s+" + DST;
+    static const std::regex IP_RE(COUNTERS);
 
-    // Skip headers.
-    for (int i = 0; i < 2; i++) {
-        std::getline(stream, statsLine, '\n');
-        extraProcessingInfo += statsLine + "\n";
-        if (statsLine.empty()) {
-            ALOGE("Empty header while parsing tethering stats");
-            return -EREMOTEIO;
+    const std::vector<std::string> lines = base::Split(statsOutput, "\n");
+    int headerLine = 0;
+    for (const std::string& line : lines) {
+        // Skip headers.
+        if (headerLine < 2) {
+            if (line.empty()) {
+                ALOGV("Empty header while parsing tethering stats");
+                return -EREMOTEIO;
+            }
+            headerLine++;
+            continue;
         }
-    }
 
-    while (std::getline(stream, statsLine, '\n')) {
-        buffPtr = statsLine.c_str();
+        if (line.empty()) continue;
 
-        /* Clean up, so a failed parse can still print info */
-        iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
-        if (strstr(buffPtr, "0.0.0.0")) {
-            // IPv4 has -- indicating what to do with fragments...
-            //       26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
-            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
-                    &packets, &bytes, iface0, iface1, rest);
-        } else {
-            // ... but IPv6 does not.
-            //       26     2373 RETURN     all      wlan0  rmnet0  ::/0                 ::/0
-            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s",
-                    &packets, &bytes, iface0, iface1, rest);
-        }
-        ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res,
-             iface0, iface1, packets, bytes, rest, buffPtr);
-        extraProcessingInfo += buffPtr;
-        extraProcessingInfo += "\n";
+        extraProcessingInfo = line;
+        std::smatch matches;
+        if (!std::regex_search(line, matches, IP_RE)) return -EREMOTEIO;
+        // Here use IP_RE to distiguish IPv4 and IPv6 iptables.
+        // IPv4 has "--" indicating what to do with fragments...
+        //		 26 	2373 RETURN     all  --  wlan0	rmnet0	0.0.0.0/0			 0.0.0.0/0
+        // ... but IPv6 does not.
+        //		 26 	2373 RETURN 	all      wlan0	rmnet0	::/0				 ::/0
+        // TODO: Replace strtoXX() calls with ParseUint() /ParseInt()
+        int64_t packets = strtoul(matches[PACKET_COUNTS].str().c_str(), nullptr, 10);
+        int64_t bytes = strtoul(matches[BYTE_COUNTS].str().c_str(), nullptr, 10);
+        std::string iface0 = matches[IFACE0_NAME].str();
+        std::string iface1 = matches[IFACE1_NAME].str();
+        std::string rest = matches[SOURCE].str();
 
-        if (res != 5) {
-            return -EREMOTEIO;
-        }
+        ALOGV("parse iface0=<%s> iface1=<%s> pkts=%" PRId64 " bytes=%" PRId64
+              " rest=<%s> orig line=<%s>",
+              iface0.c_str(), iface1.c_str(), packets, bytes, rest.c_str(), line.c_str());
         /*
          * The following assumes that the 1st rule has in:extIface out:intIface,
          * which is what TetherController sets up.
          * The 1st matches rx, and sets up the pair for the tx side.
          */
         if (!stats.intIface[0]) {
-            ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+            ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64 " rx_packets=%" PRId64
+                  " ", iface0.c_str(), iface1.c_str(), bytes, packets);
             stats.intIface = iface0;
             stats.extIface = iface1;
             stats.txPackets = packets;
             stats.txBytes = bytes;
         } else if (stats.intIface == iface1 && stats.extIface == iface0) {
-            ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
+            ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64 " rx_packets=%" PRId64
+                  " ", iface0.c_str(), iface1.c_str(), bytes, packets);
             stats.rxPackets = packets;
             stats.rxBytes = bytes;
         }