| // 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); |
| } |
| |