/* * Copyright (C) 2012 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 #include #include #include #include #include #include #include #include #define LOG_TAG "Netd" #include #include #include #include "Controllers.h" #include "NetdConstants.h" #include "IptablesRestoreController.h" int execIptablesRestoreWithOutput(IptablesTarget target, const std::string& commands, std::string *output) { return android::net::gCtls->iptablesRestoreCtrl.execute(target, commands, output); } int execIptablesRestore(IptablesTarget target, const std::string& commands) { return execIptablesRestoreWithOutput(target, commands, nullptr); } int execIptablesRestoreCommand(IptablesTarget target, const std::string& table, const std::string& command, std::string *output) { std::string fullCmd = android::base::StringPrintf("*%s\n%s\nCOMMIT\n", table.c_str(), command.c_str()); return execIptablesRestoreWithOutput(target, fullCmd, output); } /* * Check an interface name for plausibility. This should e.g. help against * directory traversal. */ bool isIfaceName(const std::string& name) { size_t i; if ((name.empty()) || (name.size() > IFNAMSIZ)) { return false; } /* First character must be alphanumeric */ if (!isalnum(name[0])) { return false; } for (i = 1; i < name.size(); i++) { if (!isalnum(name[i]) && (name[i] != '_') && (name[i] != '-') && (name[i] != ':') && (name[i] != '.')) { return false; } } return true; } int parsePrefix(const char *prefix, uint8_t *family, void *address, int size, uint8_t *prefixlen) { if (!prefix || !family || !address || !prefixlen) { return -EFAULT; } // Find the '/' separating address from prefix length. const char *slash = strchr(prefix, '/'); const char *prefixlenString = slash + 1; if (!slash || !*prefixlenString) return -EINVAL; // Convert the prefix length to a uint8_t. char *endptr; unsigned templen; templen = strtoul(prefixlenString, &endptr, 10); if (*endptr || templen > 255) { return -EINVAL; } *prefixlen = templen; // Copy the address part of the prefix to a local buffer. We have to copy // because inet_pton and getaddrinfo operate on null-terminated address // strings, but prefix is const and has '/' after the address. std::string addressString(prefix, slash - prefix); // Parse the address. addrinfo *res; addrinfo hints = { .ai_flags = AI_NUMERICHOST, }; int ret = getaddrinfo(addressString.c_str(), nullptr, &hints, &res); if (ret || !res) { return -EINVAL; // getaddrinfo return values are not errno values. } // Convert the address string to raw address bytes. void *rawAddress; int rawLength; switch (res[0].ai_family) { case AF_INET: { if (*prefixlen > 32) { return -EINVAL; } sockaddr_in *sin = (sockaddr_in *) res[0].ai_addr; rawAddress = &sin->sin_addr; rawLength = 4; break; } case AF_INET6: { if (*prefixlen > 128) { return -EINVAL; } sockaddr_in6 *sin6 = (sockaddr_in6 *) res[0].ai_addr; rawAddress = &sin6->sin6_addr; rawLength = 16; break; } default: { freeaddrinfo(res); return -EAFNOSUPPORT; } } if (rawLength > size) { freeaddrinfo(res); return -ENOSPC; } *family = res[0].ai_family; memcpy(address, rawAddress, rawLength); freeaddrinfo(res); return rawLength; } void blockSigpipe() { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGPIPE); if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) ALOGW("WARNING: SIGPIPE not blocked"); } void setCloseOnExec(const char *sock) { int fd = android_get_control_socket(sock); int flags = fcntl(fd, F_GETFD, 0); if (flags == -1) { ALOGE("Can't get fd flags for control socket %s", sock); flags = 0; } flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { ALOGE("Can't set control socket %s to FD_CLOEXEC", sock); } } // SIGTERM with timeout first, if fail, SIGKILL void stopProcess(int pid, const char* processName) { int err = kill(pid, SIGTERM); if (err) { err = errno; } if (err == ESRCH) { // This means that someone else inside netd called this helper function, // which is a programming error. There's no point in calling waitpid() here since we // know that the process is gone. ALOGE("%s child process %d unexpectedly disappeared", processName, pid); return; } if (err) { ALOGE("Error killing %s child process %d: %s", processName, pid, strerror(err)); } int status = 0; int ret = 0; for (int count = 0; ret == 0 && count < 50; count++) { usleep(100000); // sleep 0.1s to wait for process stop. ret = waitpid(pid, &status, WNOHANG); } if (ret == 0) { ALOGE("Failed to SIGTERM %s pid=%d, try SIGKILL", processName, pid); kill(pid, SIGKILL); ret = waitpid(pid, &status, 0); } if (ret == -1) { ALOGE("Error waiting for %s child process %d: %s", processName, pid, strerror(errno)); } else { ALOGD("%s process %d terminated status=%d", processName, pid, status); } }