• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "DnsTlsDispatcher"
18 //#define LOG_NDEBUG 0
19 
20 #include "DnsTlsDispatcher.h"
21 #include "DnsTlsSocketFactory.h"
22 
23 #include "log/log.h"
24 
25 namespace android {
26 namespace net {
27 
28 using netdutils::Slice;
29 
30 // static
31 std::mutex DnsTlsDispatcher::sLock;
32 
DnsTlsDispatcher()33 DnsTlsDispatcher::DnsTlsDispatcher() {
34     mFactory.reset(new DnsTlsSocketFactory());
35 }
36 
getOrderedServerList(const std::list<DnsTlsServer> & tlsServers,unsigned mark) const37 std::list<DnsTlsServer> DnsTlsDispatcher::getOrderedServerList(
38         const std::list<DnsTlsServer> &tlsServers, unsigned mark) const {
39     // Our preferred DnsTlsServer order is:
40     //     1) reuse existing IPv6 connections
41     //     2) reuse existing IPv4 connections
42     //     3) establish new IPv6 connections
43     //     4) establish new IPv4 connections
44     std::list<DnsTlsServer> existing6;
45     std::list<DnsTlsServer> existing4;
46     std::list<DnsTlsServer> new6;
47     std::list<DnsTlsServer> new4;
48 
49     // Pull out any servers for which we might have existing connections and
50     // place them at the from the list of servers to try.
51     {
52         std::lock_guard guard(sLock);
53 
54         for (const auto& tlsServer : tlsServers) {
55             const Key key = std::make_pair(mark, tlsServer);
56             if (mStore.find(key) != mStore.end()) {
57                 switch (tlsServer.ss.ss_family) {
58                     case AF_INET:
59                         existing4.push_back(tlsServer);
60                         break;
61                     case AF_INET6:
62                         existing6.push_back(tlsServer);
63                         break;
64                 }
65             } else {
66                 switch (tlsServer.ss.ss_family) {
67                     case AF_INET:
68                         new4.push_back(tlsServer);
69                         break;
70                     case AF_INET6:
71                         new6.push_back(tlsServer);
72                         break;
73                 }
74             }
75         }
76     }
77 
78     auto& out = existing6;
79     out.splice(out.cend(), existing4);
80     out.splice(out.cend(), new6);
81     out.splice(out.cend(), new4);
82     return out;
83 }
84 
query(const std::list<DnsTlsServer> & tlsServers,unsigned mark,const Slice query,const Slice ans,int * resplen)85 DnsTlsTransport::Response DnsTlsDispatcher::query(
86         const std::list<DnsTlsServer> &tlsServers, unsigned mark,
87         const Slice query, const Slice ans, int *resplen) {
88     const std::list<DnsTlsServer> orderedServers(getOrderedServerList(tlsServers, mark));
89 
90     if (orderedServers.empty()) ALOGW("Empty DnsTlsServer list");
91 
92     DnsTlsTransport::Response code = DnsTlsTransport::Response::internal_error;
93     for (const auto& server : orderedServers) {
94         code = this->query(server, mark, query, ans, resplen);
95         switch (code) {
96             // These response codes are valid responses and not expected to
97             // change if another server is queried.
98             case DnsTlsTransport::Response::success:
99             case DnsTlsTransport::Response::limit_error:
100                 return code;
101                 break;
102             // These response codes might differ when trying other servers, so
103             // keep iterating to see if we can get a different (better) result.
104             case DnsTlsTransport::Response::network_error:
105             case DnsTlsTransport::Response::internal_error:
106                 continue;
107                 break;
108             // No "default" statement.
109         }
110     }
111 
112     return code;
113 }
114 
query(const DnsTlsServer & server,unsigned mark,const Slice query,const Slice ans,int * resplen)115 DnsTlsTransport::Response DnsTlsDispatcher::query(const DnsTlsServer& server, unsigned mark,
116                                                   const Slice query,
117                                                   const Slice ans, int *resplen) {
118     const Key key = std::make_pair(mark, server);
119     Transport* xport;
120     {
121         std::lock_guard guard(sLock);
122         auto it = mStore.find(key);
123         if (it == mStore.end()) {
124             xport = new Transport(server, mark, mFactory.get());
125             mStore[key].reset(xport);
126         } else {
127             xport = it->second.get();
128         }
129         ++xport->useCount;
130     }
131 
132     ALOGV("Sending query of length %zu", query.size());
133     auto res = xport->transport.query(query);
134     ALOGV("Awaiting response");
135     const auto& result = res.get();
136     DnsTlsTransport::Response code = result.code;
137     if (code == DnsTlsTransport::Response::success) {
138         if (result.response.size() > ans.size()) {
139             ALOGV("Response too large: %zu > %zu", result.response.size(), ans.size());
140             code = DnsTlsTransport::Response::limit_error;
141         } else {
142             ALOGV("Got response successfully");
143             *resplen = result.response.size();
144             netdutils::copy(ans, netdutils::makeSlice(result.response));
145         }
146     } else {
147         ALOGV("Query failed: %u", (unsigned int) code);
148     }
149 
150     auto now = std::chrono::steady_clock::now();
151     {
152         std::lock_guard guard(sLock);
153         --xport->useCount;
154         xport->lastUsed = now;
155         cleanup(now);
156     }
157     return code;
158 }
159 
160 // This timeout effectively controls how long to keep SSL session tickets.
161 static constexpr std::chrono::minutes IDLE_TIMEOUT(5);
cleanup(std::chrono::time_point<std::chrono::steady_clock> now)162 void DnsTlsDispatcher::cleanup(std::chrono::time_point<std::chrono::steady_clock> now) {
163     // To avoid scanning mStore after every query, return early if a cleanup has been
164     // performed recently.
165     if (now - mLastCleanup < IDLE_TIMEOUT) {
166         return;
167     }
168     for (auto it = mStore.begin(); it != mStore.end();) {
169         auto& s = it->second;
170         if (s->useCount == 0 && now - s->lastUsed > IDLE_TIMEOUT) {
171             it = mStore.erase(it);
172         } else {
173             ++it;
174         }
175     }
176     mLastCleanup = now;
177 }
178 
179 }  // end of namespace net
180 }  // end of namespace android
181