blob: d88788dcf0d5effc3c0a5ad46d0e195bc57c2493 [file] [log] [blame]
// Copyright 2015 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 "android-qemu2-glue/qemu-setup.h"
#include "android/base/Log.h"
#include "android/base/network/IpAddress.h"
#include "android/base/network/Dns.h"
#include "android/utils/debug.h"
extern "C" {
#include "qemu/osdep.h"
#include "net/slirp.h"
#include "slirp/libslirp.h"
} // extern "C"
#include <vector>
#include <errno.h>
#define MAX_DNS_SERVERS 4
using android::base::IpAddress;
using android::base::Dns;
static int s_num_dns_server_addresses = 0;
static sockaddr_storage s_dns_server_addresses[MAX_DNS_SERVERS] = {};
// Convert IpAddress instance |src| into a sockaddr_storage |dst|.
// Return true on success, false on failure (invalid IpAddress type).
static bool sockaddr_storage_from_ipaddress(sockaddr_storage* dst,
const IpAddress& src) {
if (src.isIpv4()) {
auto sin = reinterpret_cast<sockaddr_in *>(dst);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(src.ipv4());
return true;
}
if (src.isIpv6()) {
auto sin6 = reinterpret_cast<sockaddr_in6 *>(dst);
sin6->sin6_family = AF_INET6;
memcpy(sin6->sin6_addr.s6_addr, src.ipv6Addr(), 16);
sin6->sin6_scope_id = htonl(src.ipv6ScopeId());
sin6->sin6_port = 0;
return true;
}
return false;
}
// Resolve host name |hostName| into a list of sockaddr_storage.
// On success, return true and append the names to |*out|. On failure
// return false, leave |*out| untouched, and sets errno.
static bool resolveHostNameToList(
const char* hostName, std::vector<sockaddr_storage>* out) {
int count = 0;
Dns::AddressList list = Dns::resolveName(hostName);
for (const auto& ip : list) {
sockaddr_storage addr = {};
if (sockaddr_storage_from_ipaddress(&addr, ip)) {
out->emplace_back(std::move(addr));
count++;
}
}
return (count > 0);
}
bool qemu_android_emulation_setup_dns_servers(const char* dns_servers,
int* pcount) {
CHECK(net_slirp_state() != nullptr) << "slirp stack should be inited!";
if (!dns_servers || !dns_servers[0]) {
// Empty list, use the default behaviour.
return 0;
}
std::vector<sockaddr_storage> server_addresses;
// Separate individual DNS server names, then resolve each one of them
// into one or more IP addresses. Support both IPv4 and IPv6 at the same
// time.
const char* p = dns_servers;
while (*p) {
const char* next_p;
const char* comma = strchr(p, ',');
if (!comma) {
comma = p + strlen(p);
next_p = comma;
} else {
next_p = comma + 1;
}
while (p < comma && *p == ' ') p++;
while (p < comma && comma[-1] == ' ') comma--;
if (comma > p) {
// Extract single server name.
std::string server(p, comma - p);
if (!resolveHostNameToList(server.c_str(), &server_addresses)) {
dwarning("Ignoring ivalid DNS address: [%s]\n", server.c_str());
}
}
p = next_p;
}
int count = static_cast<int>(server_addresses.size());
if (!count) {
return 0;
}
if (count > MAX_DNS_SERVERS) {
dwarning("Too many DNS servers, only keeping the first %d ones\n",
MAX_DNS_SERVERS);
count = MAX_DNS_SERVERS;
}
// Save it for qemu_android_emulator_init_slirp().
s_num_dns_server_addresses = count;
memcpy(s_dns_server_addresses, &server_addresses[0],
count * sizeof(server_addresses[0]));
*pcount = count;
return true;
}
void qemu_android_emulation_init_slirp(void) {
slirp_init_custom_dns_servers(static_cast<Slirp*>(net_slirp_state()),
s_dns_server_addresses,
s_num_dns_server_addresses);
}