OSDN Git Service

Change dnsmasq input command argument separator
[android-x86/system-netd.git] / server / CommandListener.cpp
index 6962cde..c190565 100644 (file)
@@ -54,10 +54,10 @@ namespace {
 const unsigned NUM_OEM_IDS = NetworkController::MAX_OEM_ID - NetworkController::MIN_OEM_ID + 1;
 
 Permission stringToPermission(const char* arg) {
-    if (!strcmp(arg, "android.permission.CHANGE_NETWORK_STATE")) {
+    if (!strcmp(arg, "NETWORK")) {
         return PERMISSION_NETWORK;
     }
-    if (!strcmp(arg, "android.permission.CONNECTIVITY_INTERNAL")) {
+    if (!strcmp(arg, "SYSTEM")) {
         return PERMISSION_SYSTEM;
     }
     return PERMISSION_NONE;
@@ -92,6 +92,7 @@ InterfaceController *CommandListener::sInterfaceCtrl = NULL;
 ResolverController *CommandListener::sResolverCtrl = NULL;
 FirewallController *CommandListener::sFirewallCtrl = NULL;
 ClatdController *CommandListener::sClatdCtrl = NULL;
+StrictController *CommandListener::sStrictCtrl = NULL;
 
 /**
  * List of module chains to be created, along with explicit ordering. ORDERING
@@ -116,6 +117,7 @@ static const char* FILTER_FORWARD[] = {
 static const char* FILTER_OUTPUT[] = {
         OEM_IPTABLES_FILTER_OUTPUT,
         FirewallController::LOCAL_OUTPUT,
+        StrictController::LOCAL_OUTPUT,
         BandwidthController::LOCAL_OUTPUT,
         NULL,
 };
@@ -132,6 +134,11 @@ static const char* MANGLE_POSTROUTING[] = {
         NULL,
 };
 
+static const char* MANGLE_FORWARD[] = {
+        NatController::LOCAL_MANGLE_FORWARD,
+        NULL,
+};
+
 static const char* NAT_PREROUTING[] = {
         OEM_IPTABLES_NAT_PREROUTING,
         NULL,
@@ -177,6 +184,7 @@ CommandListener::CommandListener() :
     registerCmd(new FirewallCmd());
     registerCmd(new ClatdCmd());
     registerCmd(new NetworkCommand());
+    registerCmd(new StrictCmd());
 
     if (!sNetCtrl)
         sNetCtrl = new NetworkController();
@@ -200,6 +208,8 @@ CommandListener::CommandListener() :
         sInterfaceCtrl = new InterfaceController();
     if (!sClatdCtrl)
         sClatdCtrl = new ClatdController(sNetCtrl);
+    if (!sStrictCtrl)
+        sStrictCtrl = new StrictController();
 
     /*
      * This is the only time we touch top-level chains in iptables; controllers
@@ -217,6 +227,7 @@ CommandListener::CommandListener() :
     createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);
     createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);
     createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);
+    createChildChains(V4, "mangle", "FORWARD", MANGLE_FORWARD);
     createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);
     createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);
 
@@ -438,6 +449,21 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                         "Failed to change IPv6 state", true);
             }
             return 0;
+        } else if (!strcmp(argv[1], "ipv6ndoffload")) {
+            if (argc != 4) {
+                cli->sendMsg(ResponseCode::CommandSyntaxError,
+                        "Usage: interface ipv6ndoffload <interface> <enable|disable>",
+                        false);
+                return 0;
+            }
+            int enable = !strncmp(argv[3], "enable", 7);
+            if (sInterfaceCtrl->setIPv6NdOffload(argv[2], enable) == 0) {
+                cli->sendMsg(ResponseCode::CommandOkay, "IPv6 ND offload changed", false);
+            } else {
+                cli->sendMsg(ResponseCode::OperationFailed,
+                        "Failed to change IPv6 ND offload state", true);
+            }
+            return 0;
         } else if (!strcmp(argv[1], "setmtu")) {
             if (argc != 4) {
                 cli->sendMsg(ResponseCode::CommandSyntaxError,
@@ -448,7 +474,7 @@ int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                 cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false);
             } else {
                 cli->sendMsg(ResponseCode::OperationFailed,
-                        "Failed to get MTU", true);
+                        "Failed to set MTU", true);
             }
             return 0;
         } else {
@@ -481,37 +507,59 @@ CommandListener::IpFwdCmd::IpFwdCmd() :
                  NetdCommand("ipfwd") {
 }
 
-int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    int rc = 0;
-
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
-        return 0;
-    }
+int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    bool matched = false;
+    bool success;
 
-    if (!strcmp(argv[1], "status")) {
-        char *tmp = NULL;
+    if (argc == 2) {
+        //   0     1
+        // ipfwd status
+        if (!strcmp(argv[1], "status")) {
+            char *tmp = NULL;
 
-        asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
-        cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
-        free(tmp);
-        return 0;
-    } else if (!strcmp(argv[1], "enable")) {
-        rc = sTetherCtrl->setIpFwdEnabled(true);
-    } else if (!strcmp(argv[1], "disable")) {
-        rc = sTetherCtrl->setIpFwdEnabled(false);
-    } else {
+            asprintf(&tmp, "Forwarding %s",
+                     ((sTetherCtrl->forwardingRequestCount() > 0) ? "enabled" : "disabled"));
+            cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
+            free(tmp);
+            return 0;
+        }
+    } else if (argc == 3) {
+        //  0      1         2
+        // ipfwd enable  <requester>
+        // ipfwd disable <requester>
+        if (!strcmp(argv[1], "enable")) {
+            matched = true;
+            success = sTetherCtrl->enableForwarding(argv[2]);
+        } else if (!strcmp(argv[1], "disable")) {
+            matched = true;
+            success = sTetherCtrl->disableForwarding(argv[2]);
+        }
+    } else if (argc == 4) {
+        //  0      1      2     3
+        // ipfwd  add   wlan0 dummy0
+        // ipfwd remove wlan0 dummy0
+        int ret = 0;
+        if (!strcmp(argv[1], "add")) {
+            matched = true;
+            ret = RouteController::enableTethering(argv[2], argv[3]);
+        } else if (!strcmp(argv[1], "remove")) {
+            matched = true;
+            ret = RouteController::disableTethering(argv[2], argv[3]);
+        }
+        success = (ret == 0);
+        errno = -ret;
+    }
+
+    if (!matched) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
         return 0;
     }
 
-    if (!rc) {
+    if (success) {
         cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
     } else {
         cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
     }
-
     return 0;
 }
 
@@ -572,19 +620,17 @@ int CommandListener::TetherCmd::runCommand(SocketClient *cli,
                 return 0;
             }
 
-            int num_addrs = argc - 2;
-            int arg_index = 2;
-            int array_index = 0;
-            in_addr *addrs = (in_addr *)malloc(sizeof(in_addr) * num_addrs);
-            while (array_index < num_addrs) {
-                if (!inet_aton(argv[arg_index++], &(addrs[array_index++]))) {
+            const int num_addrs = argc - 2;
+            // TODO: consider moving this validation into TetherController.
+            struct in_addr tmp_addr;
+            for (int arg_index = 2; arg_index < argc; arg_index++) {
+                if (!inet_aton(argv[arg_index], &tmp_addr)) {
                     cli->sendMsg(ResponseCode::CommandParameterError, "Invalid address", false);
-                    free(addrs);
                     return 0;
                 }
             }
-            rc = sTetherCtrl->startTethering(num_addrs, addrs);
-            free(addrs);
+
+            rc = sTetherCtrl->startTethering(num_addrs, &(argv[2]));
         } else if (!strcmp(argv[1], "interface")) {
             if (!strcmp(argv[2], "add")) {
                 rc = sTetherCtrl->tetherInterface(argv[3]);
@@ -771,23 +817,35 @@ int CommandListener::ResolverCmd::runCommand(SocketClient *cli, int argc, char *
     int rc = 0;
     const char **argv = const_cast<const char **>(margv);
 
-    if (argc < 2) {
+    if (argc < 3) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
         return 0;
     }
 
+    unsigned netId = stringToNetId(argv[2]);
+    // TODO: Consider making NetworkController.isValidNetwork() public
+    // and making that check here.
+
     if (!strcmp(argv[1], "setnetdns")) {
         // "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
         if (argc >= 5) {
-            rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
+            rc = sResolverCtrl->setDnsServers(netId, argv[3], &argv[4], argc - 4);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver setnetdns", false);
             return 0;
         }
+    } else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns <netId>"
+        if (argc == 3) {
+            rc = sResolverCtrl->clearDnsServers(netId);
+        } else {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                    "Wrong number of arguments to resolver clearnetdns", false);
+            return 0;
+        }
     } else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
         if (argc == 3) {
-            rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
+            rc = sResolverCtrl->flushDnsCache(netId);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver flushnet", false);
@@ -1215,8 +1273,35 @@ int CommandListener::FirewallCmd::sendGenericOkFail(SocketClient *cli, int cond)
 FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) {
     if (!strcmp(arg, "allow")) {
         return ALLOW;
-    } else {
+    } else if (!strcmp(arg, "deny")) {
         return DENY;
+    } else {
+        ALOGE("failed to parse uid rule (%s)", arg);
+        return ALLOW;
+    }
+}
+
+FirewallType CommandListener::FirewallCmd::parseFirewallType(const char* arg) {
+    if (!strcmp(arg, "whitelist")) {
+        return WHITELIST;
+    } else if (!strcmp(arg, "blacklist")) {
+        return BLACKLIST;
+    } else {
+        ALOGE("failed to parse firewall type (%s)", arg);
+        return BLACKLIST;
+    }
+}
+
+ChildChain CommandListener::FirewallCmd::parseChildChain(const char* arg) {
+    if (!strcmp(arg, "dozable")) {
+        return DOZABLE;
+    } else if (!strcmp(arg, "standby")) {
+        return STANDBY;
+    } else if (!strcmp(arg, "none")) {
+        return NONE;
+    } else {
+        ALOGE("failed to parse child firewall chain (%s)", arg);
+        return INVALID_CHAIN;
     }
 }
 
@@ -1228,7 +1313,14 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
     }
 
     if (!strcmp(argv[1], "enable")) {
-        int res = sFirewallCtrl->enableFirewall();
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                        "Usage: firewall enable <whitelist|blacklist>", false);
+            return 0;
+        }
+        FirewallType firewallType = parseFirewallType(argv[2]);
+
+        int res = sFirewallCtrl->enableFirewall(firewallType);
         return sendGenericOkFail(cli, res);
     }
     if (!strcmp(argv[1], "disable")) {
@@ -1288,17 +1380,49 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
     }
 
     if (!strcmp(argv[1], "set_uid_rule")) {
-        if (argc != 4) {
+        if (argc != 5) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
-                         "Usage: firewall set_uid_rule <1000> <allow|deny>",
+                         "Usage: firewall set_uid_rule <dozable|standby|none> <1000> <allow|deny>",
                          false);
             return 0;
         }
 
-        int uid = atoi(argv[2]);
-        FirewallRule rule = parseRule(argv[3]);
+        ChildChain childChain = parseChildChain(argv[2]);
+        if (childChain == INVALID_CHAIN) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Invalid chain name. Valid names are: <dozable|standby|none>",
+                         false);
+            return 0;
+        }
+        int uid = atoi(argv[3]);
+        FirewallRule rule = parseRule(argv[4]);
+        int res = sFirewallCtrl->setUidRule(childChain, uid, rule);
+        return sendGenericOkFail(cli, res);
+    }
+
+    if (!strcmp(argv[1], "enable_chain")) {
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: firewall enable_chain <dozable|standby>",
+                         false);
+            return 0;
+        }
+
+        ChildChain childChain = parseChildChain(argv[2]);
+        int res = sFirewallCtrl->enableChildChains(childChain, true);
+        return sendGenericOkFail(cli, res);
+    }
 
-        int res = sFirewallCtrl->setUidRule(uid, rule);
+    if (!strcmp(argv[1], "disable_chain")) {
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: firewall disable_chain <dozable|standby>",
+                         false);
+            return 0;
+        }
+
+        ChildChain childChain = parseChildChain(argv[2]);
+        int res = sFirewallCtrl->enableChildChains(childChain, false);
         return sendGenericOkFail(cli, res);
     }
 
@@ -1312,26 +1436,21 @@ CommandListener::ClatdCmd::ClatdCmd() : NetdCommand("clatd") {
 int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
                                                             char **argv) {
     int rc = 0;
-    if (argc < 2) {
+    if (argc < 3) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
         return 0;
     }
 
-    if(!strcmp(argv[1], "stop")) {
-        rc = sClatdCtrl->stopClatd();
+    if (!strcmp(argv[1], "stop")) {
+        rc = sClatdCtrl->stopClatd(argv[2]);
     } else if (!strcmp(argv[1], "status")) {
         char *tmp = NULL;
-
-        asprintf(&tmp, "Clatd status: %s", (sClatdCtrl->isClatdStarted() ?
-                                                        "started" : "stopped"));
+        asprintf(&tmp, "Clatd status: %s", (sClatdCtrl->isClatdStarted(argv[2]) ?
+                                            "started" : "stopped"));
         cli->sendMsg(ResponseCode::ClatdStatusResult, tmp, false);
         free(tmp);
         return 0;
-    } else if(!strcmp(argv[1], "start")) {
-        if (argc < 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
-            return 0;
-        }
+    } else if (!strcmp(argv[1], "start")) {
         rc = sClatdCtrl->startClatd(argv[2]);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown clatd cmd", false);
@@ -1347,6 +1466,76 @@ int CommandListener::ClatdCmd::runCommand(SocketClient *cli, int argc,
     return 0;
 }
 
+CommandListener::StrictCmd::StrictCmd() :
+    NetdCommand("strict") {
+}
+
+int CommandListener::StrictCmd::sendGenericOkFail(SocketClient *cli, int cond) {
+    if (!cond) {
+        cli->sendMsg(ResponseCode::CommandOkay, "Strict command succeeded", false);
+    } else {
+        cli->sendMsg(ResponseCode::OperationFailed, "Strict command failed", false);
+    }
+    return 0;
+}
+
+StrictPenalty CommandListener::StrictCmd::parsePenalty(const char* arg) {
+    if (!strcmp(arg, "reject")) {
+        return REJECT;
+    } else if (!strcmp(arg, "log")) {
+        return LOG;
+    } else if (!strcmp(arg, "accept")) {
+        return ACCEPT;
+    } else {
+        return INVALID;
+    }
+}
+
+int CommandListener::StrictCmd::runCommand(SocketClient *cli, int argc,
+        char **argv) {
+    if (argc < 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
+        return 0;
+    }
+
+    if (!strcmp(argv[1], "enable")) {
+        int res = sStrictCtrl->enableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+    if (!strcmp(argv[1], "disable")) {
+        int res = sStrictCtrl->disableStrict();
+        return sendGenericOkFail(cli, res);
+    }
+
+    if (!strcmp(argv[1], "set_uid_cleartext_policy")) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: strict set_uid_cleartext_policy <uid> <accept|log|reject>",
+                         false);
+            return 0;
+        }
+
+        errno = 0;
+        unsigned long int uid = strtoul(argv[2], NULL, 0);
+        if (errno || uid > UID_MAX) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid UID", false);
+            return 0;
+        }
+
+        StrictPenalty penalty = parsePenalty(argv[3]);
+        if (penalty == INVALID) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid penalty argument", false);
+            return 0;
+        }
+
+        int res = sStrictCtrl->setUidCleartextPenalty((uid_t) uid, penalty);
+        return sendGenericOkFail(cli, res);
+    }
+
+    cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
+    return 0;
+}
+
 CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
 }
 
@@ -1372,9 +1561,11 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         return syntaxError(client, "Missing argument");
     }
 
-    //    0      1      2      3      4       5         6            7               8
-    // network route [legacy <uid>]  add   <netId> <interface> <destination> [nexthop|"unreachable"]
-    // network route [legacy <uid>] remove <netId> <interface> <destination> [nexthop|"unreachable"]
+    //    0      1      2      3      4       5         6            7           8
+    // network route [legacy <uid>]  add   <netId> <interface> <destination> [nexthop]
+    // network route [legacy <uid>] remove <netId> <interface> <destination> [nexthop]
+    //
+    // nexthop may be either an IPv4/IPv6 address or one of "unreachable" or "throw".
     if (!strcmp(argv[1], "route")) {
         if (argc < 6 || argc > 9) {
             return syntaxError(client, "Incorrect number of arguments");
@@ -1532,24 +1723,36 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc,
         if (nextArg == argc) {
             return syntaxError(client, "Missing id");
         }
+
+        bool userPermissions = !strcmp(argv[2], "user");
+        bool networkPermissions = !strcmp(argv[2], "network");
+        if (!userPermissions && !networkPermissions) {
+            return syntaxError(client, "Unknown argument");
+        }
+
         std::vector<unsigned> ids;
         for (; nextArg < argc; ++nextArg) {
-            char* endPtr;
-            unsigned id = strtoul(argv[nextArg], &endPtr, 0);
-            if (!*argv[nextArg] || *endPtr) {
-                return syntaxError(client, "Invalid id");
+            if (userPermissions) {
+                char* endPtr;
+                unsigned id = strtoul(argv[nextArg], &endPtr, 0);
+                if (!*argv[nextArg] || *endPtr) {
+                    return syntaxError(client, "Invalid id");
+                }
+                ids.push_back(id);
+            } else {
+                // networkPermissions
+                ids.push_back(stringToNetId(argv[nextArg]));
             }
-            ids.push_back(id);
         }
-        if (!strcmp(argv[2], "user")) {
+        if (userPermissions) {
             sNetCtrl->setPermissionForUsers(permission, ids);
-        } else if (!strcmp(argv[2], "network")) {
+        } else {
+            // networkPermissions
             if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) {
                 return operationError(client, "setPermissionForNetworks() failed", ret);
             }
-        } else {
-            return syntaxError(client, "Unknown argument");
         }
+
         return success(client);
     }