blob: ff5b5567bc0c2adf8aaff73f64167326a9f60a2f [file] [log] [blame]
/*
* Copyright (C) 2016 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 "dns_responder_client.h"
#include <android-base/stringprintf.h>
// TODO: make this dynamic and stop depending on implementation details.
#define TEST_OEM_NETWORK "oem29"
#define TEST_NETID 30
// TODO: move this somewhere shared.
static const char* ANDROID_DNS_MODE = "ANDROID_DNS_MODE";
// The only response code used in this class. See
// frameworks/base/services/java/com/android/server/NetworkManagementService.java
// for others.
static constexpr int ResponseCodeOK = 200;
using android::base::StringPrintf;
static int netdCommand(const char* sockname, const char* command) {
int sock = socket_local_client(sockname,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (sock < 0) {
perror("Error connecting");
return -1;
}
// FrameworkListener expects the whole command in one read.
char buffer[256];
int nwritten = snprintf(buffer, sizeof(buffer), "0 %s", command);
if (write(sock, buffer, nwritten + 1) < 0) {
perror("Error sending netd command");
close(sock);
return -1;
}
int nread = read(sock, buffer, sizeof(buffer));
if (nread < 0) {
perror("Error reading response");
close(sock);
return -1;
}
close(sock);
return atoi(buffer);
}
static bool expectNetdResult(int expected, const char* sockname, const char* format, ...) {
char command[256];
va_list args;
va_start(args, format);
vsnprintf(command, sizeof(command), format, args);
va_end(args);
int result = netdCommand(sockname, command);
if (expected != result) {
return false;
}
return (200 <= expected && expected < 300);
}
void DnsResponderClient::SetupMappings(unsigned num_hosts, const std::vector<std::string>& domains,
std::vector<Mapping>* mappings) {
mappings->resize(num_hosts * domains.size());
auto mappings_it = mappings->begin();
for (unsigned i = 0 ; i < num_hosts ; ++i) {
for (const auto& domain : domains) {
mappings_it->host = StringPrintf("host%u", i);
mappings_it->entry = StringPrintf("%s.%s.", mappings_it->host.c_str(),
domain.c_str());
mappings_it->ip4 = StringPrintf("192.0.2.%u", i%253 + 1);
mappings_it->ip6 = StringPrintf("2001:db8::%x", i%65534 + 1);
++mappings_it;
}
}
}
bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& servers,
const std::vector<std::string>& domains, const std::vector<int>& params) {
auto rv = mNetdSrv->setResolverConfiguration(TEST_NETID, servers, domains, params);
return rv.isOk();
}
bool DnsResponderClient::SetResolversForNetwork(const std::vector<std::string>& searchDomains,
const std::vector<std::string>& servers, const std::string& params) {
std::string cmd = StringPrintf("resolver setnetdns %d \"", mOemNetId);
if (!searchDomains.empty()) {
cmd += searchDomains[0].c_str();
for (size_t i = 1 ; i < searchDomains.size() ; ++i) {
cmd += " ";
cmd += searchDomains[i];
}
}
cmd += "\"";
for (const auto& str : servers) {
cmd += " ";
cmd += str;
}
if (!params.empty()) {
cmd += " --params \"";
cmd += params;
cmd += "\"";
}
int rv = netdCommand("netd", cmd.c_str());
if (rv != ResponseCodeOK) {
return false;
}
return true;
}
void DnsResponderClient::SetupDNSServers(unsigned num_servers, const std::vector<Mapping>& mappings,
std::vector<std::unique_ptr<test::DNSResponder>>* dns,
std::vector<std::string>* servers) {
const char* listen_srv = "53";
dns->resize(num_servers);
servers->resize(num_servers);
for (unsigned i = 0 ; i < num_servers ; ++i) {
auto& server = (*servers)[i];
auto& d = (*dns)[i];
server = StringPrintf("127.0.0.%u", i + 100);
d = std::make_unique<test::DNSResponder>(server, listen_srv, 250,
ns_rcode::ns_r_servfail, 1.0);
for (const auto& mapping : mappings) {
d->addMapping(mapping.entry.c_str(), ns_type::ns_t_a, mapping.ip4.c_str());
d->addMapping(mapping.entry.c_str(), ns_type::ns_t_aaaa, mapping.ip6.c_str());
}
d->startServer();
}
}
void DnsResponderClient::ShutdownDNSServers(std::vector<std::unique_ptr<test::DNSResponder>>* dns) {
for (const auto& d : *dns) {
d->stopServer();
}
dns->clear();
}
int DnsResponderClient::SetupOemNetwork() {
netdCommand("netd", "network destroy " TEST_OEM_NETWORK);
if (!expectNetdResult(ResponseCodeOK, "netd",
"network create %s", TEST_OEM_NETWORK)) {
return -1;
}
int oemNetId = TEST_NETID;
setNetworkForProcess(oemNetId);
if ((unsigned) oemNetId != getNetworkForProcess()) {
return -1;
}
return oemNetId;
}
void DnsResponderClient::TearDownOemNetwork(int oemNetId) {
if (oemNetId != -1) {
expectNetdResult(ResponseCodeOK, "netd",
"network destroy %s", TEST_OEM_NETWORK);
}
}
void DnsResponderClient::SetUp() {
// Ensure resolutions go via proxy.
setenv(ANDROID_DNS_MODE, "", 1);
mOemNetId = SetupOemNetwork();
// binder setup
auto binder = android::defaultServiceManager()->getService(android::String16("netd"));
mNetdSrv = android::interface_cast<android::net::INetd>(binder);
}
void DnsResponderClient::TearDown() {
TearDownOemNetwork(mOemNetId);
}