1 // Copyright 2015 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "libwebserv/dbus_protocol_handler.h"
16
17 #include <tuple>
18
19 #include <base/logging.h>
20 #include <brillo/map_utils.h>
21 #include <brillo/streams/file_stream.h>
22 #include <brillo/streams/stream_utils.h>
23
24 #include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
25 #include "libwebserv/dbus_server.h"
26 #include "libwebserv/protocol_handler.h"
27 #include "libwebserv/request.h"
28 #include "libwebserv/request_handler_callback.h"
29 #include "libwebserv/response_impl.h"
30 #include "webservd/dbus-proxies.h"
31
32 namespace libwebserv {
33
34 namespace {
35
36 // Dummy callback for async D-Bus errors.
IgnoreDBusError(brillo::Error *)37 void IgnoreDBusError(brillo::Error* /* error */) {}
38
39 // Copies the data from |src_stream| to the destination stream represented
40 // by a file descriptor |fd|.
WriteResponseData(brillo::StreamPtr src_stream,const dbus::FileDescriptor & fd)41 void WriteResponseData(brillo::StreamPtr src_stream,
42 const dbus::FileDescriptor& fd) {
43 int dupfd = dup(fd.value());
44 auto dest_stream =
45 brillo::FileStream::FromFileDescriptor(dupfd, true, nullptr);
46 CHECK(dest_stream);
47 // Dummy callbacks for success/error of data-copy operation. We ignore both
48 // notifications here.
49 auto on_success = [](brillo::StreamPtr, brillo::StreamPtr, uint64_t) {};
50 auto on_error = [](brillo::StreamPtr, brillo::StreamPtr,
51 const brillo::Error*) {};
52 brillo::stream_utils::CopyData(
53 std::move(src_stream), std::move(dest_stream), base::Bind(on_success),
54 base::Bind(on_error));
55 }
56
57 } // anonymous namespace
58
DBusProtocolHandler(const std::string & name,DBusServer * server)59 DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
60 DBusServer* server)
61 : name_{name}, server_{server} {
62 }
63
~DBusProtocolHandler()64 DBusProtocolHandler::~DBusProtocolHandler() {
65 // Remove any existing handlers, so the web server knows that we don't
66 // need them anymore.
67
68 // We need to get a copy of the map keys since removing the handlers will
69 // modify the map in the middle of the loop and that's not a good thing.
70 auto handler_ids = brillo::GetMapKeys(request_handlers_);
71 for (int handler_id : handler_ids) {
72 RemoveHandler(handler_id);
73 }
74 }
IsConnected() const75 bool DBusProtocolHandler::IsConnected() const {
76 return !proxies_.empty();
77 }
78
GetName() const79 std::string DBusProtocolHandler::GetName() const {
80 return name_;
81 }
82
GetPorts() const83 std::set<uint16_t> DBusProtocolHandler::GetPorts() const {
84 std::set<uint16_t> ports;
85 for (const auto& pair : proxies_)
86 ports.insert(pair.second->port());
87 return ports;
88 }
89
GetProtocols() const90 std::set<std::string> DBusProtocolHandler::GetProtocols() const {
91 std::set<std::string> protocols;
92 for (const auto& pair : proxies_)
93 protocols.insert(pair.second->protocol());
94 return protocols;
95 }
96
GetCertificateFingerprint() const97 brillo::Blob DBusProtocolHandler::GetCertificateFingerprint() const {
98 brillo::Blob fingerprint;
99 for (const auto& pair : proxies_) {
100 fingerprint = pair.second->certificate_fingerprint();
101 if (!fingerprint.empty())
102 break;
103 }
104 return fingerprint;
105 }
106
AddHandler(const std::string & url,const std::string & method,std::unique_ptr<RequestHandlerInterface> handler)107 int DBusProtocolHandler::AddHandler(
108 const std::string& url,
109 const std::string& method,
110 std::unique_ptr<RequestHandlerInterface> handler) {
111 request_handlers_.emplace(
112 ++last_handler_id_,
113 HandlerMapEntry{url, method,
114 std::map<ProtocolHandlerProxyInterface*, std::string>{},
115 std::move(handler)});
116 // For each instance of remote protocol handler object sharing the same name,
117 // add the request handler.
118 for (const auto& pair : proxies_) {
119 pair.second->AddRequestHandlerAsync(
120 url,
121 method,
122 server_->service_name_,
123 base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
124 weak_ptr_factory_.GetWeakPtr(),
125 last_handler_id_,
126 pair.second),
127 base::Bind(&DBusProtocolHandler::AddHandlerError,
128 weak_ptr_factory_.GetWeakPtr(),
129 last_handler_id_));
130 }
131 return last_handler_id_;
132 }
133
AddHandlerCallback(const std::string & url,const std::string & method,const base::Callback<RequestHandlerInterface::HandlerSignature> & handler_callback)134 int DBusProtocolHandler::AddHandlerCallback(
135 const std::string& url,
136 const std::string& method,
137 const base::Callback<RequestHandlerInterface::HandlerSignature>&
138 handler_callback) {
139 std::unique_ptr<RequestHandlerInterface> handler{
140 new RequestHandlerCallback{handler_callback}};
141 return AddHandler(url, method, std::move(handler));
142 }
143
RemoveHandler(int handler_id)144 bool DBusProtocolHandler::RemoveHandler(int handler_id) {
145 auto p = request_handlers_.find(handler_id);
146 if (p == request_handlers_.end())
147 return false;
148
149 for (const auto& pair : p->second.remote_handler_ids) {
150 pair.first->RemoveRequestHandlerAsync(
151 pair.second,
152 base::Bind(&base::DoNothing),
153 base::Bind(&IgnoreDBusError));
154 }
155
156 request_handlers_.erase(p);
157 return true;
158 }
159
Connect(ProtocolHandlerProxyInterface * proxy)160 void DBusProtocolHandler::Connect(ProtocolHandlerProxyInterface* proxy) {
161 proxies_.emplace(proxy->GetObjectPath(), proxy);
162 for (const auto& pair : request_handlers_) {
163 proxy->AddRequestHandlerAsync(
164 pair.second.url,
165 pair.second.method,
166 server_->service_name_,
167 base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
168 weak_ptr_factory_.GetWeakPtr(),
169 pair.first,
170 proxy),
171 base::Bind(&DBusProtocolHandler::AddHandlerError,
172 weak_ptr_factory_.GetWeakPtr(),
173 pair.first));
174 }
175 }
176
Disconnect(const dbus::ObjectPath & object_path)177 void DBusProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
178 proxies_.erase(object_path);
179 if (proxies_.empty())
180 remote_handler_id_map_.clear();
181 for (auto& pair : request_handlers_)
182 pair.second.remote_handler_ids.clear();
183 }
184
AddHandlerSuccess(int handler_id,ProtocolHandlerProxyInterface * proxy,const std::string & remote_handler_id)185 void DBusProtocolHandler::AddHandlerSuccess(
186 int handler_id,
187 ProtocolHandlerProxyInterface* proxy,
188 const std::string& remote_handler_id) {
189 auto p = request_handlers_.find(handler_id);
190 CHECK(p != request_handlers_.end());
191 p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
192
193 remote_handler_id_map_.emplace(remote_handler_id, handler_id);
194 }
195
AddHandlerError(int,brillo::Error *)196 void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
197 brillo::Error* /* error */) {
198 // Nothing to do at the moment.
199 }
200
ProcessRequest(const std::string & protocol_handler_id,const std::string & remote_handler_id,const std::string & request_id,std::unique_ptr<Request> request,brillo::ErrorPtr * error)201 bool DBusProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
202 const std::string& remote_handler_id,
203 const std::string& request_id,
204 std::unique_ptr<Request> request,
205 brillo::ErrorPtr* error) {
206 request_id_map_.emplace(request_id, protocol_handler_id);
207 auto id_iter = remote_handler_id_map_.find(remote_handler_id);
208 if (id_iter == remote_handler_id_map_.end()) {
209 brillo::Error::AddToPrintf(error, FROM_HERE,
210 brillo::errors::dbus::kDomain,
211 DBUS_ERROR_FAILED,
212 "Unknown request handler '%s'",
213 remote_handler_id.c_str());
214 return false;
215 }
216 auto handler_iter = request_handlers_.find(id_iter->second);
217 if (handler_iter == request_handlers_.end()) {
218 brillo::Error::AddToPrintf(error, FROM_HERE,
219 brillo::errors::dbus::kDomain,
220 DBUS_ERROR_FAILED,
221 "Handler # %d is no longer available",
222 id_iter->second);
223 return false;
224 }
225 handler_iter->second.handler->HandleRequest(
226 std::move(request),
227 std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
228 return true;
229 }
230
CompleteRequest(const std::string & request_id,int status_code,const std::multimap<std::string,std::string> & headers,brillo::StreamPtr data_stream)231 void DBusProtocolHandler::CompleteRequest(
232 const std::string& request_id,
233 int status_code,
234 const std::multimap<std::string, std::string>& headers,
235 brillo::StreamPtr data_stream) {
236 ProtocolHandlerProxyInterface* proxy =
237 GetRequestProtocolHandlerProxy(request_id);
238 if (!proxy)
239 return;
240
241 std::vector<std::tuple<std::string, std::string>> header_list;
242 header_list.reserve(headers.size());
243 for (const auto& pair : headers)
244 header_list.emplace_back(pair.first, pair.second);
245
246 int64_t data_size = -1;
247 if (data_stream->CanGetSize())
248 data_size = data_stream->GetRemainingSize();
249 proxy->CompleteRequestAsync(
250 request_id, status_code, header_list, data_size,
251 base::Bind(&WriteResponseData, base::Passed(&data_stream)),
252 base::Bind(&IgnoreDBusError));
253 }
254
GetFileData(const std::string & request_id,int file_id,const base::Callback<void (brillo::StreamPtr)> & success_callback,const base::Callback<void (brillo::Error *)> & error_callback)255 void DBusProtocolHandler::GetFileData(
256 const std::string& request_id,
257 int file_id,
258 const base::Callback<void(brillo::StreamPtr)>& success_callback,
259 const base::Callback<void(brillo::Error*)>& error_callback) {
260 ProtocolHandlerProxyInterface* proxy =
261 GetRequestProtocolHandlerProxy(request_id);
262 CHECK(proxy);
263
264 // Store the success/error callback in a shared object so it can be referenced
265 // by the two wrapper callbacks. Since the original callbacks MAY contain
266 // move-only types, copying the base::Callback object is generally unsafe and
267 // may destroy the source object of the copy (despite the fact that it is
268 // constant). So, here we move both callbacks to |Callbacks| structure and
269 // use a shared pointer to it in both success and error callback wrappers.
270 struct Callbacks {
271 base::Callback<void(brillo::StreamPtr)> on_success;
272 base::Callback<void(brillo::Error*)> on_error;
273 };
274 auto callbacks = std::make_shared<Callbacks>();
275 callbacks->on_success = success_callback;
276 callbacks->on_error = error_callback;
277
278 auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
279 brillo::ErrorPtr error;
280 // Unfortunately there is no way to take ownership of the file descriptor
281 // since |fd| is a const reference, so duplicate the descriptor.
282 int dupfd = dup(fd.value());
283 auto stream = brillo::FileStream::FromFileDescriptor(dupfd, true, &error);
284 if (!stream)
285 return callbacks->on_error.Run(error.get());
286 callbacks->on_success.Run(std::move(stream));
287 };
288 auto on_error = [callbacks](brillo::Error* error) {
289 callbacks->on_error.Run(error);
290 };
291
292 proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
293 base::Bind(on_error));
294 }
295
296 DBusProtocolHandler::ProtocolHandlerProxyInterface*
GetRequestProtocolHandlerProxy(const std::string & request_id) const297 DBusProtocolHandler::GetRequestProtocolHandlerProxy(
298 const std::string& request_id) const {
299 auto iter = request_id_map_.find(request_id);
300 if (iter == request_id_map_.end()) {
301 LOG(ERROR) << "Can't find pending request with ID " << request_id;
302 return nullptr;
303 }
304 std::string handler_id = iter->second;
305 auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
306 return pair.second->id() == handler_id;
307 };
308 auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
309 find_proxy_by_id);
310 if (proxy_iter == proxies_.end()) {
311 LOG(WARNING) << "Completing a request after the handler proxy is removed";
312 return nullptr;
313 }
314 return proxy_iter->second;
315 }
316
317
318 } // namespace libwebserv
319