Loading DnsProxyListener.cpp +68 −101 Original line number Diff line number Diff line Loading @@ -557,21 +557,18 @@ void DnsProxyListener::Handler::spawn() { delete this; } DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints, DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, std::string host, std::string service, std::unique_ptr<addrinfo> hints, const android_net_context& netcontext) : Handler(c), mHost(host), mService(service), mHints(hints), mNetContext(netcontext) {} DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() { free(mHost); free(mService); free(mHints); } : Handler(c), mHost(move(host)), mService(move(service)), mHints(move(hints)), mNetContext(netcontext) {} static bool evaluate_domain_name(const android_net_context &netcontext, const char *host) { if (!gResNetdCallbacks.evaluate_domain_name) return true; static bool evaluate_domain_name(const android_net_context& netcontext, const char* host) { if (!gResNetdCallbacks.evaluate_domain_name) return true; return gResNetdCallbacks.evaluate_domain_name(netcontext, host); } Loading Loading @@ -628,11 +625,8 @@ static bool sendaddrinfo(SocketClient* c, addrinfo* ai) { // Write the struct piece by piece because we might be a 64-bit netd // talking to a 32-bit process. bool success = sendBE32(c, ai->ai_flags) && sendBE32(c, ai->ai_family) && sendBE32(c, ai->ai_socktype) && sendBE32(c, ai->ai_protocol); bool success = sendBE32(c, ai->ai_flags) && sendBE32(c, ai->ai_family) && sendBE32(c, ai->ai_socktype) && sendBE32(c, ai->ai_protocol); if (!success) { return false; } Loading @@ -652,8 +646,6 @@ static bool sendaddrinfo(SocketClient* c, addrinfo* ai) { void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event) { if (mHost == nullptr) return; const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA); const bool unspecWantedButNoIPv6 = ((!mHints || mHints->ai_family == AF_UNSPEC) && *rv == 0 && onlyIPv4Answers(*res)); Loading @@ -671,10 +663,12 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis const uid_t uid = mClient->getUid(); if (queryLimiter.start(uid)) { const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); mHints->ai_family = AF_INET; // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous // DNS lookup is failed with error EAI_NODATA. *rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, res, event); *rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, res, event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code Loading Loading @@ -712,9 +706,10 @@ void DnsProxyListener::GetAddrInfoHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { if (evaluate_domain_name(mNetContext, mHost)) { rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result, &event); const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); if (evaluate_domain_name(mNetContext, host)) { rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, &result, &event); } else { rv = EAI_SYSTEM; } Loading Loading @@ -782,8 +777,7 @@ void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* ad DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() : FrameworkCommand("getaddrinfo") {} int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 8) { Loading @@ -795,21 +789,8 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, return -1; } char* name = argv[1]; if (strcmp("^", name) == 0) { name = nullptr; } else { name = strdup(name); } char* service = argv[2]; if (strcmp("^", service) == 0) { service = nullptr; } else { service = strdup(service); } addrinfo* hints = nullptr; const std::string name = argv[1]; const std::string service = argv[2]; int ai_flags = strtol(argv[3], nullptr, 10); int ai_family = strtol(argv[4], nullptr, 10); int ai_socktype = strtol(argv[5], nullptr, 10); Loading @@ -825,16 +806,16 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS; } if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { hints = (addrinfo*) calloc(1, sizeof(addrinfo)); std::unique_ptr<addrinfo> hints; if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { hints.reset((addrinfo*)calloc(1, sizeof(addrinfo))); hints->ai_flags = ai_flags; hints->ai_family = ai_family; hints->ai_socktype = ai_socktype; hints->ai_protocol = ai_protocol; } (new GetAddrInfoHandler(cli, name, service, hints, netcontext))->spawn(); (new GetAddrInfoHandler(cli, name, service, move(hints), netcontext))->spawn(); return 0; } Loading Loading @@ -1043,8 +1024,7 @@ int DnsProxyListener::GetDnsNetIdCommand::runCommand(SocketClient* cli, int argc *******************************************************/ DnsProxyListener::GetHostByNameCmd::GetHostByNameCmd() : FrameworkCommand("gethostbyname") {} int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 4) { Loading @@ -1059,15 +1039,9 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, uid_t uid = cli->getUid(); unsigned netId = strtoul(argv[1], nullptr, 10); const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId); char* name = argv[2]; std::string name = argv[2]; int af = strtol(argv[3], nullptr, 10); if (strcmp(name, "^") == 0) { name = nullptr; } else { name = strdup(name); } android_net_context netcontext; gResNetdCallbacks.get_network_context(netId, uid, &netcontext); Loading @@ -1079,13 +1053,10 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, return 0; } DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, char* name, int af, DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, std::string name, int af, const android_net_context& netcontext) : Handler(c), mName(name), mAf(af), mNetContext(netcontext) {} DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() { free(mName); } : Handler(c), mName(move(name)), mAf(af), mNetContext(netcontext) {} void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, hostent* hbuf, char* buf, size_t buflen, struct hostent** hpp, Loading @@ -1106,7 +1077,8 @@ void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, hoste // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis const uid_t uid = mClient->getUid(); if (queryLimiter.start(uid)) { *rv = resolv_gethostbyname(mName, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); *rv = resolv_gethostbyname(name, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code Loading Loading @@ -1135,8 +1107,9 @@ void DnsProxyListener::GetHostByNameHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { if (evaluate_domain_name(mNetContext, mName)) { rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); if (evaluate_domain_name(mNetContext, name)) { rv = resolv_gethostbyname(name, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); } else { rv = EAI_SYSTEM; Loading Loading @@ -1184,8 +1157,7 @@ std::string DnsProxyListener::GetHostByNameHandler::threadName() { *******************************************************/ DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() : FrameworkCommand("gethostbyaddr") {} int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 5) { Loading @@ -1204,15 +1176,14 @@ int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, unsigned netId = strtoul(argv[4], nullptr, 10); const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId); void* addr = malloc(sizeof(in6_addr)); in6_addr addr; errno = 0; int result = inet_pton(addrFamily, addrStr, addr); int result = inet_pton(addrFamily, addrStr, &addr); if (result <= 0) { char* msg = nullptr; asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno)); LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null"); cli->sendMsg(ResponseCode::OperationFailed, msg, false); free(addr); free(msg); return -1; } Loading @@ -1228,7 +1199,7 @@ int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, return 0; } DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, void* address, DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, in6_addr address, int addressLen, int addressFamily, const android_net_context& netcontext) : Handler(c), Loading @@ -1237,15 +1208,11 @@ DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, vo mAddressFamily(addressFamily), mNetContext(netcontext) {} DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() { free(mAddress); } void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(hostent* hbuf, char* buf, size_t buflen, struct hostent** hpp, NetworkDnsEventReported* event) { if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) { if (*hpp != nullptr || mAddressFamily != AF_INET6) { return; } Loading @@ -1260,7 +1227,7 @@ void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(hostent* hbuf, struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); struct sockaddr_in6* v6prefix = (struct sockaddr_in6*)&ss; struct in6_addr v6addr = *(in6_addr*) mAddress; struct in6_addr v6addr = mAddress; // Check if address has NAT64 prefix. Only /96 IPv6 NAT64 prefixes are supported if ((v6addr.s6_addr32[0] != v6prefix->sin6_addr.s6_addr32[0]) || (v6addr.s6_addr32[1] != v6prefix->sin6_addr.s6_addr32[1]) || Loading Loading @@ -1300,7 +1267,7 @@ void DnsProxyListener::GetHostByAddrHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { rv = resolv_gethostbyaddr(mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf, rv = resolv_gethostbyaddr(&mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); queryLimiter.finish(uid); } else { Loading DnsProxyListener.h +12 −12 Original line number Diff line number Diff line Loading @@ -65,9 +65,9 @@ class DnsProxyListener : public FrameworkListener { class GetAddrInfoHandler : public Handler { public: // Note: All of host, service, and hints may be NULL GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints, const android_net_context& netcontext); ~GetAddrInfoHandler() override; GetAddrInfoHandler(SocketClient* c, std::string host, std::string service, std::unique_ptr<addrinfo> hints, const android_net_context& netcontext); ~GetAddrInfoHandler() override = default; void run() override; std::string threadName() override; Loading @@ -75,9 +75,9 @@ class DnsProxyListener : public FrameworkListener { private: void doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event); char* mHost; // owned. TODO: convert to std::string. char* mService; // owned. TODO: convert to std::string. addrinfo* mHints; // owned std::string mHost; std::string mService; std::unique_ptr<addrinfo> mHints; android_net_context mNetContext; }; Loading @@ -91,9 +91,9 @@ class DnsProxyListener : public FrameworkListener { class GetHostByNameHandler : public Handler { public: GetHostByNameHandler(SocketClient* c, char* name, int af, GetHostByNameHandler(SocketClient* c, std::string name, int af, const android_net_context& netcontext); ~GetHostByNameHandler() override; ~GetHostByNameHandler() override = default; void run() override; std::string threadName() override; Loading @@ -102,7 +102,7 @@ class DnsProxyListener : public FrameworkListener { void doDns64Synthesis(int32_t* rv, hostent* hbuf, char* buf, size_t buflen, hostent** hpp, NetworkDnsEventReported* event); char* mName; // owned. TODO: convert to std::string. std::string mName; int mAf; android_net_context mNetContext; }; Loading @@ -117,9 +117,9 @@ class DnsProxyListener : public FrameworkListener { class GetHostByAddrHandler : public Handler { public: GetHostByAddrHandler(SocketClient* c, void* address, int addressLen, int addressFamily, GetHostByAddrHandler(SocketClient* c, in6_addr address, int addressLen, int addressFamily, const android_net_context& netcontext); ~GetHostByAddrHandler() override; ~GetHostByAddrHandler() override = default; void run() override; std::string threadName() override; Loading @@ -128,7 +128,7 @@ class DnsProxyListener : public FrameworkListener { void doDns64ReverseLookup(hostent* hbuf, char* buf, size_t buflen, hostent** hpp, NetworkDnsEventReported* event); void* mAddress; // address to lookup; owned in6_addr mAddress; int mAddressLen; // length of address to look up int mAddressFamily; // address family android_net_context mNetContext; Loading OperationLimiter.h +28 −11 Original line number Diff line number Diff line Loading @@ -33,39 +33,44 @@ namespace netdutils { // The intended usage pattern is: // OperationLimiter<UserId> connections_per_user; // ... // // Before opening a new connection // if (!limiter.start(user)) { // return error; // } else { // // open the connection // // ...do some work... // // close the connection // limiter.finish(user); // int connectToSomeResource(int user) { // if (!connections_per_user.start(user)) return TRY_AGAIN_LATER; // // ...do expensive work here... // connections_per_user.finish(user); // } // // This class is thread-safe. template <typename KeyType> class OperationLimiter { public: explicit OperationLimiter(int limit) : mLimitPerKey(limit) {} OperationLimiter(int limitPerKey, int globalLimit = INT_MAX) : mLimitPerKey(limitPerKey), mGlobalLimit(globalLimit) {} ~OperationLimiter() { DCHECK(mCounters.empty()) << "Destroying OperationLimiter with active operations"; } // Returns false if |key| has reached the maximum number of concurrent // operations, otherwise increments the counter and returns true. // Returns false if |key| has reached the maximum number of concurrent operations, // or if the global limit has been reached. Otherwise, increments the counter and returns true. // // Note: each successful start(key) must be matched by exactly one call to // finish(key). bool start(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); if (mGlobalCounter >= mGlobalLimit) { // Oh, no! return false; } auto& cnt = mCounters[key]; // operator[] creates new entries as needed. if (cnt >= mLimitPerKey) { // Oh, no! return false; } ++cnt; ++mGlobalCounter; return true; } Loading @@ -73,6 +78,13 @@ class OperationLimiter { // See usage notes on start(). void finish(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); --mGlobalCounter; if (mGlobalCounter < 0) { LOG(FATAL_WITHOUT_ABORT) << "Global operations counter going negative, this is a bug."; return; } auto it = mCounters.find(key); if (it == mCounters.end()) { LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key; Loading @@ -93,8 +105,13 @@ class OperationLimiter { // Tracks the number of outstanding queries by key. std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex); int mGlobalCounter GUARDED_BY(mMutex) = 0; // Maximum number of outstanding queries from a single key. const int mLimitPerKey; // Maximum number of outstanding queries, globally. const int mGlobalLimit; }; } // namespace netdutils Loading gethnamaddr.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -388,8 +388,11 @@ nospc: int resolv_gethostbyname(const char* name, int af, hostent* hp, char* buf, size_t buflen, const android_net_context* netcontext, hostent** result, NetworkDnsEventReported* event) { getnamaddr info; if (name == nullptr || hp == nullptr) { return EAI_SYSTEM; } getnamaddr info; ResState res; res_init(&res, netcontext, event); Loading tests/resolv_integration_test.cpp +14 −1 Original line number Diff line number Diff line Loading @@ -393,7 +393,7 @@ TEST_F(ResolverTest, GetHostByName) { result = gethostbyname("nonexistent"); EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, nonexistent_host_name)); ASSERT_TRUE(result == nullptr); ASSERT_EQ(HOST_NOT_FOUND, h_errno); EXPECT_EQ(HOST_NOT_FOUND, h_errno); dns.clearQueries(); result = gethostbyname("hello"); Loading @@ -405,6 +405,19 @@ TEST_F(ResolverTest, GetHostByName) { EXPECT_TRUE(result->h_addr_list[1] == nullptr); } TEST_F(ResolverTest, GetHostByName_NULL) { // Most libc implementations would just crash on gethostbyname(NULL). Instead, Bionic // serializes the null argument over dnsproxyd, causing the server-side to crash! // This is a regression test. const char* const testcases[] = {nullptr, "", "^"}; for (const char* name : testcases) { SCOPED_TRACE(fmt::format("gethostbyname({})", name ? name : "NULL")); const hostent* result = gethostbyname(name); EXPECT_TRUE(result == nullptr); EXPECT_EQ(HOST_NOT_FOUND, h_errno); } } TEST_F(ResolverTest, GetHostByName_cnames) { constexpr char host_name[] = "host.example.com."; size_t cnamecount = 0; Loading Loading
DnsProxyListener.cpp +68 −101 Original line number Diff line number Diff line Loading @@ -557,21 +557,18 @@ void DnsProxyListener::Handler::spawn() { delete this; } DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints, DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient* c, std::string host, std::string service, std::unique_ptr<addrinfo> hints, const android_net_context& netcontext) : Handler(c), mHost(host), mService(service), mHints(hints), mNetContext(netcontext) {} DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() { free(mHost); free(mService); free(mHints); } : Handler(c), mHost(move(host)), mService(move(service)), mHints(move(hints)), mNetContext(netcontext) {} static bool evaluate_domain_name(const android_net_context &netcontext, const char *host) { if (!gResNetdCallbacks.evaluate_domain_name) return true; static bool evaluate_domain_name(const android_net_context& netcontext, const char* host) { if (!gResNetdCallbacks.evaluate_domain_name) return true; return gResNetdCallbacks.evaluate_domain_name(netcontext, host); } Loading Loading @@ -628,11 +625,8 @@ static bool sendaddrinfo(SocketClient* c, addrinfo* ai) { // Write the struct piece by piece because we might be a 64-bit netd // talking to a 32-bit process. bool success = sendBE32(c, ai->ai_flags) && sendBE32(c, ai->ai_family) && sendBE32(c, ai->ai_socktype) && sendBE32(c, ai->ai_protocol); bool success = sendBE32(c, ai->ai_flags) && sendBE32(c, ai->ai_family) && sendBE32(c, ai->ai_socktype) && sendBE32(c, ai->ai_protocol); if (!success) { return false; } Loading @@ -652,8 +646,6 @@ static bool sendaddrinfo(SocketClient* c, addrinfo* ai) { void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event) { if (mHost == nullptr) return; const bool ipv6WantedButNoData = (mHints && mHints->ai_family == AF_INET6 && *rv == EAI_NODATA); const bool unspecWantedButNoIPv6 = ((!mHints || mHints->ai_family == AF_UNSPEC) && *rv == 0 && onlyIPv4Answers(*res)); Loading @@ -671,10 +663,12 @@ void DnsProxyListener::GetAddrInfoHandler::doDns64Synthesis(int32_t* rv, addrinf // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis const uid_t uid = mClient->getUid(); if (queryLimiter.start(uid)) { const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); mHints->ai_family = AF_INET; // Don't need to do freeaddrinfo(res) before starting new DNS lookup because previous // DNS lookup is failed with error EAI_NODATA. *rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, res, event); *rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, res, event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code Loading Loading @@ -712,9 +706,10 @@ void DnsProxyListener::GetAddrInfoHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { if (evaluate_domain_name(mNetContext, mHost)) { rv = resolv_getaddrinfo(mHost, mService, mHints, &mNetContext, &result, &event); const char* host = mHost.starts_with('^') ? nullptr : mHost.c_str(); const char* service = mService.starts_with('^') ? nullptr : mService.c_str(); if (evaluate_domain_name(mNetContext, host)) { rv = resolv_getaddrinfo(host, service, mHints.get(), &mNetContext, &result, &event); } else { rv = EAI_SYSTEM; } Loading Loading @@ -782,8 +777,7 @@ void addIpAddrWithinLimit(std::vector<std::string>* ip_addrs, const sockaddr* ad DnsProxyListener::GetAddrInfoCmd::GetAddrInfoCmd() : FrameworkCommand("getaddrinfo") {} int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 8) { Loading @@ -795,21 +789,8 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, return -1; } char* name = argv[1]; if (strcmp("^", name) == 0) { name = nullptr; } else { name = strdup(name); } char* service = argv[2]; if (strcmp("^", service) == 0) { service = nullptr; } else { service = strdup(service); } addrinfo* hints = nullptr; const std::string name = argv[1]; const std::string service = argv[2]; int ai_flags = strtol(argv[3], nullptr, 10); int ai_family = strtol(argv[4], nullptr, 10); int ai_socktype = strtol(argv[5], nullptr, 10); Loading @@ -825,16 +806,16 @@ int DnsProxyListener::GetAddrInfoCmd::runCommand(SocketClient *cli, netcontext.flags |= NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS; } if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { hints = (addrinfo*) calloc(1, sizeof(addrinfo)); std::unique_ptr<addrinfo> hints; if (ai_flags != -1 || ai_family != -1 || ai_socktype != -1 || ai_protocol != -1) { hints.reset((addrinfo*)calloc(1, sizeof(addrinfo))); hints->ai_flags = ai_flags; hints->ai_family = ai_family; hints->ai_socktype = ai_socktype; hints->ai_protocol = ai_protocol; } (new GetAddrInfoHandler(cli, name, service, hints, netcontext))->spawn(); (new GetAddrInfoHandler(cli, name, service, move(hints), netcontext))->spawn(); return 0; } Loading Loading @@ -1043,8 +1024,7 @@ int DnsProxyListener::GetDnsNetIdCommand::runCommand(SocketClient* cli, int argc *******************************************************/ DnsProxyListener::GetHostByNameCmd::GetHostByNameCmd() : FrameworkCommand("gethostbyname") {} int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 4) { Loading @@ -1059,15 +1039,9 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, uid_t uid = cli->getUid(); unsigned netId = strtoul(argv[1], nullptr, 10); const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId); char* name = argv[2]; std::string name = argv[2]; int af = strtol(argv[3], nullptr, 10); if (strcmp(name, "^") == 0) { name = nullptr; } else { name = strdup(name); } android_net_context netcontext; gResNetdCallbacks.get_network_context(netId, uid, &netcontext); Loading @@ -1079,13 +1053,10 @@ int DnsProxyListener::GetHostByNameCmd::runCommand(SocketClient *cli, return 0; } DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, char* name, int af, DnsProxyListener::GetHostByNameHandler::GetHostByNameHandler(SocketClient* c, std::string name, int af, const android_net_context& netcontext) : Handler(c), mName(name), mAf(af), mNetContext(netcontext) {} DnsProxyListener::GetHostByNameHandler::~GetHostByNameHandler() { free(mName); } : Handler(c), mName(move(name)), mAf(af), mNetContext(netcontext) {} void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, hostent* hbuf, char* buf, size_t buflen, struct hostent** hpp, Loading @@ -1106,7 +1077,8 @@ void DnsProxyListener::GetHostByNameHandler::doDns64Synthesis(int32_t* rv, hoste // If caller wants IPv6 answers but no data, try to query IPv4 answers for synthesis const uid_t uid = mClient->getUid(); if (queryLimiter.start(uid)) { *rv = resolv_gethostbyname(mName, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); *rv = resolv_gethostbyname(name, AF_INET, hbuf, buf, buflen, &mNetContext, hpp, event); queryLimiter.finish(uid); if (*rv) { *rv = EAI_NODATA; // return original error code Loading Loading @@ -1135,8 +1107,9 @@ void DnsProxyListener::GetHostByNameHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { if (evaluate_domain_name(mNetContext, mName)) { rv = resolv_gethostbyname(mName, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, const char* name = mName.starts_with('^') ? nullptr : mName.c_str(); if (evaluate_domain_name(mNetContext, name)) { rv = resolv_gethostbyname(name, mAf, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); } else { rv = EAI_SYSTEM; Loading Loading @@ -1184,8 +1157,7 @@ std::string DnsProxyListener::GetHostByNameHandler::threadName() { *******************************************************/ DnsProxyListener::GetHostByAddrCmd::GetHostByAddrCmd() : FrameworkCommand("gethostbyaddr") {} int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, int argc, char **argv) { int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient* cli, int argc, char** argv) { logArguments(argc, argv); if (argc != 5) { Loading @@ -1204,15 +1176,14 @@ int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, unsigned netId = strtoul(argv[4], nullptr, 10); const bool useLocalNameservers = checkAndClearUseLocalNameserversFlag(&netId); void* addr = malloc(sizeof(in6_addr)); in6_addr addr; errno = 0; int result = inet_pton(addrFamily, addrStr, addr); int result = inet_pton(addrFamily, addrStr, &addr); if (result <= 0) { char* msg = nullptr; asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno)); LOG(WARNING) << "GetHostByAddrCmd::runCommand: " << (msg ? msg : "null"); cli->sendMsg(ResponseCode::OperationFailed, msg, false); free(addr); free(msg); return -1; } Loading @@ -1228,7 +1199,7 @@ int DnsProxyListener::GetHostByAddrCmd::runCommand(SocketClient *cli, return 0; } DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, void* address, DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, in6_addr address, int addressLen, int addressFamily, const android_net_context& netcontext) : Handler(c), Loading @@ -1237,15 +1208,11 @@ DnsProxyListener::GetHostByAddrHandler::GetHostByAddrHandler(SocketClient* c, vo mAddressFamily(addressFamily), mNetContext(netcontext) {} DnsProxyListener::GetHostByAddrHandler::~GetHostByAddrHandler() { free(mAddress); } void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(hostent* hbuf, char* buf, size_t buflen, struct hostent** hpp, NetworkDnsEventReported* event) { if (*hpp != nullptr || mAddressFamily != AF_INET6 || !mAddress) { if (*hpp != nullptr || mAddressFamily != AF_INET6) { return; } Loading @@ -1260,7 +1227,7 @@ void DnsProxyListener::GetHostByAddrHandler::doDns64ReverseLookup(hostent* hbuf, struct sockaddr_storage ss = netdutils::IPSockAddr(prefix.ip()); struct sockaddr_in6* v6prefix = (struct sockaddr_in6*)&ss; struct in6_addr v6addr = *(in6_addr*) mAddress; struct in6_addr v6addr = mAddress; // Check if address has NAT64 prefix. Only /96 IPv6 NAT64 prefixes are supported if ((v6addr.s6_addr32[0] != v6prefix->sin6_addr.s6_addr32[0]) || (v6addr.s6_addr32[1] != v6prefix->sin6_addr.s6_addr32[1]) || Loading Loading @@ -1300,7 +1267,7 @@ void DnsProxyListener::GetHostByAddrHandler::run() { NetworkDnsEventReported event; initDnsEvent(&event, mNetContext); if (queryLimiter.start(uid)) { rv = resolv_gethostbyaddr(mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf, rv = resolv_gethostbyaddr(&mAddress, mAddressLen, mAddressFamily, &hbuf, tmpbuf, sizeof tmpbuf, &mNetContext, &hp, &event); queryLimiter.finish(uid); } else { Loading
DnsProxyListener.h +12 −12 Original line number Diff line number Diff line Loading @@ -65,9 +65,9 @@ class DnsProxyListener : public FrameworkListener { class GetAddrInfoHandler : public Handler { public: // Note: All of host, service, and hints may be NULL GetAddrInfoHandler(SocketClient* c, char* host, char* service, addrinfo* hints, const android_net_context& netcontext); ~GetAddrInfoHandler() override; GetAddrInfoHandler(SocketClient* c, std::string host, std::string service, std::unique_ptr<addrinfo> hints, const android_net_context& netcontext); ~GetAddrInfoHandler() override = default; void run() override; std::string threadName() override; Loading @@ -75,9 +75,9 @@ class DnsProxyListener : public FrameworkListener { private: void doDns64Synthesis(int32_t* rv, addrinfo** res, NetworkDnsEventReported* event); char* mHost; // owned. TODO: convert to std::string. char* mService; // owned. TODO: convert to std::string. addrinfo* mHints; // owned std::string mHost; std::string mService; std::unique_ptr<addrinfo> mHints; android_net_context mNetContext; }; Loading @@ -91,9 +91,9 @@ class DnsProxyListener : public FrameworkListener { class GetHostByNameHandler : public Handler { public: GetHostByNameHandler(SocketClient* c, char* name, int af, GetHostByNameHandler(SocketClient* c, std::string name, int af, const android_net_context& netcontext); ~GetHostByNameHandler() override; ~GetHostByNameHandler() override = default; void run() override; std::string threadName() override; Loading @@ -102,7 +102,7 @@ class DnsProxyListener : public FrameworkListener { void doDns64Synthesis(int32_t* rv, hostent* hbuf, char* buf, size_t buflen, hostent** hpp, NetworkDnsEventReported* event); char* mName; // owned. TODO: convert to std::string. std::string mName; int mAf; android_net_context mNetContext; }; Loading @@ -117,9 +117,9 @@ class DnsProxyListener : public FrameworkListener { class GetHostByAddrHandler : public Handler { public: GetHostByAddrHandler(SocketClient* c, void* address, int addressLen, int addressFamily, GetHostByAddrHandler(SocketClient* c, in6_addr address, int addressLen, int addressFamily, const android_net_context& netcontext); ~GetHostByAddrHandler() override; ~GetHostByAddrHandler() override = default; void run() override; std::string threadName() override; Loading @@ -128,7 +128,7 @@ class DnsProxyListener : public FrameworkListener { void doDns64ReverseLookup(hostent* hbuf, char* buf, size_t buflen, hostent** hpp, NetworkDnsEventReported* event); void* mAddress; // address to lookup; owned in6_addr mAddress; int mAddressLen; // length of address to look up int mAddressFamily; // address family android_net_context mNetContext; Loading
OperationLimiter.h +28 −11 Original line number Diff line number Diff line Loading @@ -33,39 +33,44 @@ namespace netdutils { // The intended usage pattern is: // OperationLimiter<UserId> connections_per_user; // ... // // Before opening a new connection // if (!limiter.start(user)) { // return error; // } else { // // open the connection // // ...do some work... // // close the connection // limiter.finish(user); // int connectToSomeResource(int user) { // if (!connections_per_user.start(user)) return TRY_AGAIN_LATER; // // ...do expensive work here... // connections_per_user.finish(user); // } // // This class is thread-safe. template <typename KeyType> class OperationLimiter { public: explicit OperationLimiter(int limit) : mLimitPerKey(limit) {} OperationLimiter(int limitPerKey, int globalLimit = INT_MAX) : mLimitPerKey(limitPerKey), mGlobalLimit(globalLimit) {} ~OperationLimiter() { DCHECK(mCounters.empty()) << "Destroying OperationLimiter with active operations"; } // Returns false if |key| has reached the maximum number of concurrent // operations, otherwise increments the counter and returns true. // Returns false if |key| has reached the maximum number of concurrent operations, // or if the global limit has been reached. Otherwise, increments the counter and returns true. // // Note: each successful start(key) must be matched by exactly one call to // finish(key). bool start(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); if (mGlobalCounter >= mGlobalLimit) { // Oh, no! return false; } auto& cnt = mCounters[key]; // operator[] creates new entries as needed. if (cnt >= mLimitPerKey) { // Oh, no! return false; } ++cnt; ++mGlobalCounter; return true; } Loading @@ -73,6 +78,13 @@ class OperationLimiter { // See usage notes on start(). void finish(KeyType key) EXCLUDES(mMutex) { std::lock_guard lock(mMutex); --mGlobalCounter; if (mGlobalCounter < 0) { LOG(FATAL_WITHOUT_ABORT) << "Global operations counter going negative, this is a bug."; return; } auto it = mCounters.find(key); if (it == mCounters.end()) { LOG(FATAL_WITHOUT_ABORT) << "Decremented non-existent counter for key=" << key; Loading @@ -93,8 +105,13 @@ class OperationLimiter { // Tracks the number of outstanding queries by key. std::unordered_map<KeyType, int> mCounters GUARDED_BY(mMutex); int mGlobalCounter GUARDED_BY(mMutex) = 0; // Maximum number of outstanding queries from a single key. const int mLimitPerKey; // Maximum number of outstanding queries, globally. const int mGlobalLimit; }; } // namespace netdutils Loading
gethnamaddr.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -388,8 +388,11 @@ nospc: int resolv_gethostbyname(const char* name, int af, hostent* hp, char* buf, size_t buflen, const android_net_context* netcontext, hostent** result, NetworkDnsEventReported* event) { getnamaddr info; if (name == nullptr || hp == nullptr) { return EAI_SYSTEM; } getnamaddr info; ResState res; res_init(&res, netcontext, event); Loading
tests/resolv_integration_test.cpp +14 −1 Original line number Diff line number Diff line Loading @@ -393,7 +393,7 @@ TEST_F(ResolverTest, GetHostByName) { result = gethostbyname("nonexistent"); EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, nonexistent_host_name)); ASSERT_TRUE(result == nullptr); ASSERT_EQ(HOST_NOT_FOUND, h_errno); EXPECT_EQ(HOST_NOT_FOUND, h_errno); dns.clearQueries(); result = gethostbyname("hello"); Loading @@ -405,6 +405,19 @@ TEST_F(ResolverTest, GetHostByName) { EXPECT_TRUE(result->h_addr_list[1] == nullptr); } TEST_F(ResolverTest, GetHostByName_NULL) { // Most libc implementations would just crash on gethostbyname(NULL). Instead, Bionic // serializes the null argument over dnsproxyd, causing the server-side to crash! // This is a regression test. const char* const testcases[] = {nullptr, "", "^"}; for (const char* name : testcases) { SCOPED_TRACE(fmt::format("gethostbyname({})", name ? name : "NULL")); const hostent* result = gethostbyname(name); EXPECT_TRUE(result == nullptr); EXPECT_EQ(HOST_NOT_FOUND, h_errno); } } TEST_F(ResolverTest, GetHostByName_cnames) { constexpr char host_name[] = "host.example.com."; size_t cnamecount = 0; Loading