1 /*
2 * Copyright (c) 2017, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the web server of border router
32 */
33
34 #define OTBR_LOG_TAG "WEB"
35
36 #include "web/web-service/web_server.hpp"
37
38 #define BOOST_NO_CXX11_SCOPED_ENUMS
39 #include <boost/filesystem.hpp>
40 #undef BOOST_NO_CXX11_SCOPED_ENUMS
41
42 #include <server_http.hpp>
43
44 #include "common/code_utils.hpp"
45 #include "common/logging.hpp"
46
47 #define OT_ADD_PREFIX_PATH "^/add_prefix"
48 #define OT_AVAILABLE_NETWORK_PATH "^/available_network$"
49 #define OT_DELETE_PREFIX_PATH "^/delete_prefix"
50 #define OT_FORM_NETWORK_PATH "^/form_network$"
51 #define OT_GET_NETWORK_PATH "^/get_properties$"
52 #define OT_JOIN_NETWORK_PATH "^/join_network$"
53 #define OT_GET_QRCODE_PATH "^/get_qrcode$"
54 #define OT_SET_NETWORK_PATH "^/settings$"
55 #define OT_COMMISSIONER_START_PATH "^/commission$"
56 #define OT_REQUEST_METHOD_GET "GET"
57 #define OT_REQUEST_METHOD_POST "POST"
58 #define OT_RESPONSE_SUCCESS_STATUS "HTTP/1.1 200 OK\r\n"
59 #define OT_RESPONSE_HEADER_LENGTH "Content-Length: "
60 #define OT_RESPONSE_HEADER_CSS_TYPE "\r\nContent-Type: text/css"
61 #define OT_RESPONSE_HEADER_TEXT_HTML_TYPE "\r\nContent-Type: text/html; charset=utf-8"
62 #define OT_RESPONSE_HEADER_TYPE "Content-Type: application/json\r\n charset=utf-8"
63 #define OT_RESPONSE_PLACEHOLD "\r\n\r\n"
64 #define OT_RESPONSE_FAILURE_STATUS "HTTP/1.1 400 Bad Request\r\n"
65 #define OT_BUFFER_SIZE 1024
66
67 namespace otbr {
68 namespace Web {
69
EscapeHtml(std::string & content)70 static void EscapeHtml(std::string &content)
71 {
72 std::string output;
73
74 output.reserve(content.size());
75 for (char c : content)
76 {
77 switch (c)
78 {
79 case '&':
80 output.append("&");
81 break;
82 case '<':
83 output.append("<");
84 break;
85 case '>':
86 output.append(">");
87 break;
88 case '"':
89 output.append(""");
90 break;
91 case '\'':
92 output.append("'");
93 break;
94 default:
95 output.push_back(c);
96 break;
97 }
98 }
99
100 output.swap(content);
101 }
102
WebServer(void)103 WebServer::WebServer(void)
104 : mServer(new HttpServer())
105 {
106 }
107
~WebServer(void)108 WebServer::~WebServer(void)
109 {
110 delete mServer;
111 }
112
Init()113 void WebServer::Init()
114 {
115 std::string networkName, extPanId;
116
117 if (mWpanService.GetWpanServiceStatus(networkName, extPanId) > 0)
118 {
119 return;
120 }
121 }
122
StartWebServer(const char * aIfName,const char * aListenAddr,uint16_t aPort)123 void WebServer::StartWebServer(const char *aIfName, const char *aListenAddr, uint16_t aPort)
124 {
125 if (aListenAddr != nullptr)
126 {
127 mServer->config.address = aListenAddr;
128 }
129 mServer->config.port = aPort;
130 mWpanService.SetInterfaceName(aIfName);
131 Init();
132 ResponseGetQRCode();
133 ResponseJoinNetwork();
134 ResponseFormNetwork();
135 ResponseAddOnMeshPrefix();
136 ResponseDeleteOnMeshPrefix();
137 ResponseGetStatus();
138 ResponseGetAvailableNetwork();
139 ResponseCommission();
140 DefaultHttpResponse();
141
142 try
143 {
144 mServer->start();
145 } catch (const std::exception &e)
146 {
147 otbrLogCrit("failed to start web server: %s", e.what());
148 abort();
149 }
150 }
151
StopWebServer(void)152 void WebServer::StopWebServer(void)
153 {
154 try
155 {
156 mServer->stop();
157 } catch (const std::exception &e)
158 {
159 otbrLogCrit("failed to stop web server: %s", e.what());
160 }
161 }
162
HandleHttpRequest(const char * aUrl,const char * aMethod,HttpRequestCallback aCallback)163 void WebServer::HandleHttpRequest(const char *aUrl, const char *aMethod, HttpRequestCallback aCallback)
164 {
165 mServer->resource[aUrl][aMethod] = [aCallback, this](std::shared_ptr<HttpServer::Response> response,
166 std::shared_ptr<HttpServer::Request> request) {
167 try
168 {
169 std::string httpResponse;
170 if (aCallback != nullptr)
171 {
172 httpResponse = aCallback(request->content.string(), this);
173 }
174
175 *response << OT_RESPONSE_SUCCESS_STATUS << OT_RESPONSE_HEADER_LENGTH << httpResponse.length()
176 << OT_RESPONSE_PLACEHOLD << httpResponse;
177 } catch (std::exception &e)
178 {
179 std::string content = e.what();
180 EscapeHtml(content);
181 *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << strlen(e.what())
182 << OT_RESPONSE_PLACEHOLD << content;
183 }
184 };
185 }
186
DefaultResourceSend(const HttpServer & aServer,const std::shared_ptr<HttpServer::Response> & aResponse,const std::shared_ptr<std::ifstream> & aIfStream)187 void DefaultResourceSend(const HttpServer &aServer,
188 const std::shared_ptr<HttpServer::Response> &aResponse,
189 const std::shared_ptr<std::ifstream> &aIfStream)
190 {
191 static std::vector<char> buffer(OT_BUFFER_SIZE); // Safe when server is running on one thread
192
193 std::streamsize readLength;
194
195 if ((readLength = aIfStream->read(&buffer[0], buffer.size()).gcount()) > 0)
196 {
197 aResponse->write(&buffer[0], readLength);
198 if (readLength == static_cast<std::streamsize>(buffer.size()))
199 {
200 aResponse->send([&aServer, aResponse, aIfStream](const boost::system::error_code &ec) {
201 if (!ec)
202 {
203 DefaultResourceSend(aServer, aResponse, aIfStream);
204 }
205 else
206 {
207 std::cerr << "Connection interrupted" << std::endl;
208 }
209 });
210 }
211 }
212 }
213
DefaultHttpResponse(void)214 void WebServer::DefaultHttpResponse(void)
215 {
216 mServer->default_resource[OT_REQUEST_METHOD_GET] = [this](std::shared_ptr<HttpServer::Response> response,
217 std::shared_ptr<HttpServer::Request> request) {
218 try
219 {
220 auto webRootPath = boost::filesystem::canonical(WEB_FILE_PATH);
221 auto path = boost::filesystem::canonical(webRootPath / request->path);
222
223 // Check if path is within webRootPath
224 if (std::distance(webRootPath.begin(), webRootPath.end()) > std::distance(path.begin(), path.end()) ||
225 !std::equal(webRootPath.begin(), webRootPath.end(), path.begin()))
226 {
227 throw std::invalid_argument("path must be within root path");
228 }
229 if (boost::filesystem::is_directory(path))
230 {
231 path /= "index.html";
232 }
233 if (!(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)))
234 {
235 throw std::invalid_argument("file does not exist");
236 }
237
238 std::string cacheControl, etag;
239
240 auto ifs = std::make_shared<std::ifstream>();
241 ifs->open(path.string(), std::ifstream::in | std::ios::binary | std::ios::ate);
242 std::string extension = path.extension().string();
243 std::string header = "";
244 if (extension == ".css")
245 {
246 header = OT_RESPONSE_HEADER_CSS_TYPE;
247 }
248 else if (extension == ".html")
249 {
250 header = OT_RESPONSE_HEADER_TEXT_HTML_TYPE;
251 }
252
253 if (*ifs)
254 {
255 auto length = ifs->tellg();
256 ifs->seekg(0, std::ios::beg);
257
258 *response << OT_RESPONSE_SUCCESS_STATUS << cacheControl << etag << OT_RESPONSE_HEADER_LENGTH << length
259 << header << OT_RESPONSE_PLACEHOLD;
260
261 DefaultResourceSend(*mServer, response, ifs);
262 }
263 else
264 {
265 throw std::invalid_argument("could not read file");
266 }
267
268 } catch (const std::exception &e)
269 {
270 std::string content = "Could not open path `" + request->path + "`: " + e.what();
271 EscapeHtml(content);
272 *response << OT_RESPONSE_FAILURE_STATUS << OT_RESPONSE_HEADER_LENGTH << content.length()
273 << OT_RESPONSE_PLACEHOLD << content;
274 }
275 };
276 }
277
HandleJoinNetworkRequest(const std::string & aJoinRequest,void * aUserData)278 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest, void *aUserData)
279 {
280 WebServer *webServer = static_cast<WebServer *>(aUserData);
281
282 return webServer->HandleJoinNetworkRequest(aJoinRequest);
283 }
284
HandleGetQRCodeRequest(const std::string & aGetQRCodeRequest,void * aUserData)285 std::string WebServer::HandleGetQRCodeRequest(const std::string &aGetQRCodeRequest, void *aUserData)
286 {
287 WebServer *webServer = static_cast<WebServer *>(aUserData);
288
289 return webServer->HandleGetQRCodeRequest(aGetQRCodeRequest);
290 }
291
HandleFormNetworkRequest(const std::string & aFormRequest,void * aUserData)292 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest, void *aUserData)
293 {
294 WebServer *webServer = static_cast<WebServer *>(aUserData);
295
296 return webServer->HandleFormNetworkRequest(aFormRequest);
297 }
298
HandleAddPrefixRequest(const std::string & aAddPrefixRequest,void * aUserData)299 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest, void *aUserData)
300 {
301 WebServer *webServer = static_cast<WebServer *>(aUserData);
302
303 return webServer->HandleAddPrefixRequest(aAddPrefixRequest);
304 }
305
HandleDeletePrefixRequest(const std::string & aDeletePrefixRequest,void * aUserData)306 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest, void *aUserData)
307 {
308 WebServer *webServer = static_cast<WebServer *>(aUserData);
309
310 return webServer->HandleDeletePrefixRequest(aDeletePrefixRequest);
311 }
312
HandleGetStatusRequest(const std::string & aGetStatusRequest,void * aUserData)313 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest, void *aUserData)
314 {
315 WebServer *webServer = static_cast<WebServer *>(aUserData);
316
317 return webServer->HandleGetStatusRequest(aGetStatusRequest);
318 }
319
HandleGetAvailableNetworkResponse(const std::string & aGetAvailableNetworkRequest,void * aUserData)320 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest,
321 void *aUserData)
322 {
323 WebServer *webServer = static_cast<WebServer *>(aUserData);
324
325 return webServer->HandleGetAvailableNetworkResponse(aGetAvailableNetworkRequest);
326 }
327
HandleCommission(const std::string & aCommissionRequest,void * aUserData)328 std::string WebServer::HandleCommission(const std::string &aCommissionRequest, void *aUserData)
329 {
330 WebServer *webServer = static_cast<WebServer *>(aUserData);
331
332 return webServer->HandleCommission(aCommissionRequest);
333 }
334
ResponseJoinNetwork(void)335 void WebServer::ResponseJoinNetwork(void)
336 {
337 HandleHttpRequest(OT_JOIN_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleJoinNetworkRequest);
338 }
339
ResponseGetQRCode(void)340 void WebServer::ResponseGetQRCode(void)
341 {
342 HandleHttpRequest(OT_GET_QRCODE_PATH, OT_REQUEST_METHOD_GET, HandleGetQRCodeRequest);
343 }
344
ResponseFormNetwork(void)345 void WebServer::ResponseFormNetwork(void)
346 {
347 HandleHttpRequest(OT_FORM_NETWORK_PATH, OT_REQUEST_METHOD_POST, HandleFormNetworkRequest);
348 }
349
ResponseAddOnMeshPrefix(void)350 void WebServer::ResponseAddOnMeshPrefix(void)
351 {
352 HandleHttpRequest(OT_ADD_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleAddPrefixRequest);
353 }
354
ResponseDeleteOnMeshPrefix(void)355 void WebServer::ResponseDeleteOnMeshPrefix(void)
356 {
357 HandleHttpRequest(OT_DELETE_PREFIX_PATH, OT_REQUEST_METHOD_POST, HandleDeletePrefixRequest);
358 }
359
ResponseGetStatus(void)360 void WebServer::ResponseGetStatus(void)
361 {
362 HandleHttpRequest(OT_GET_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetStatusRequest);
363 }
364
ResponseGetAvailableNetwork(void)365 void WebServer::ResponseGetAvailableNetwork(void)
366 {
367 HandleHttpRequest(OT_AVAILABLE_NETWORK_PATH, OT_REQUEST_METHOD_GET, HandleGetAvailableNetworkResponse);
368 }
369
ResponseCommission(void)370 void WebServer::ResponseCommission(void)
371 {
372 HandleHttpRequest(OT_COMMISSIONER_START_PATH, OT_REQUEST_METHOD_POST, HandleCommission);
373 }
374
HandleJoinNetworkRequest(const std::string & aJoinRequest)375 std::string WebServer::HandleJoinNetworkRequest(const std::string &aJoinRequest)
376 {
377 return mWpanService.HandleJoinNetworkRequest(aJoinRequest);
378 }
379
HandleGetQRCodeRequest(const std::string & aGetQRCodeRequest)380 std::string WebServer::HandleGetQRCodeRequest(const std::string &aGetQRCodeRequest)
381 {
382 OTBR_UNUSED_VARIABLE(aGetQRCodeRequest);
383 return mWpanService.HandleGetQRCodeRequest();
384 }
385
HandleFormNetworkRequest(const std::string & aFormRequest)386 std::string WebServer::HandleFormNetworkRequest(const std::string &aFormRequest)
387 {
388 return mWpanService.HandleFormNetworkRequest(aFormRequest);
389 }
390
HandleAddPrefixRequest(const std::string & aAddPrefixRequest)391 std::string WebServer::HandleAddPrefixRequest(const std::string &aAddPrefixRequest)
392 {
393 return mWpanService.HandleAddPrefixRequest(aAddPrefixRequest);
394 }
395
HandleDeletePrefixRequest(const std::string & aDeletePrefixRequest)396 std::string WebServer::HandleDeletePrefixRequest(const std::string &aDeletePrefixRequest)
397 {
398 return mWpanService.HandleDeletePrefixRequest(aDeletePrefixRequest);
399 }
400
HandleGetStatusRequest(const std::string & aGetStatusRequest)401 std::string WebServer::HandleGetStatusRequest(const std::string &aGetStatusRequest)
402 {
403 OTBR_UNUSED_VARIABLE(aGetStatusRequest);
404 return mWpanService.HandleStatusRequest();
405 }
406
HandleGetAvailableNetworkResponse(const std::string & aGetAvailableNetworkRequest)407 std::string WebServer::HandleGetAvailableNetworkResponse(const std::string &aGetAvailableNetworkRequest)
408 {
409 OTBR_UNUSED_VARIABLE(aGetAvailableNetworkRequest);
410 return mWpanService.HandleAvailableNetworkRequest();
411 }
412
HandleCommission(const std::string & aCommissionRequest)413 std::string WebServer::HandleCommission(const std::string &aCommissionRequest)
414 {
415 return mWpanService.HandleCommission(aCommissionRequest);
416 }
417
418 } // namespace Web
419 } // namespace otbr
420