1 /*
2 * Copyright (c) 2020, 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 #define OTBR_LOG_TAG "REST"
30
31 #include "rest/rest_web_server.hpp"
32
33 #include <cerrno>
34
35 #include <fcntl.h>
36
37 #include "utils/socket_utils.hpp"
38
39 using std::chrono::duration_cast;
40 using std::chrono::microseconds;
41 using std::chrono::steady_clock;
42
43 namespace otbr {
44 namespace rest {
45
46 // Maximum number of connection a server support at the same time.
47 static const uint32_t kMaxServeNum = 500;
48 // Port number used by Rest server.
49 static const uint32_t kPortNumber = 8081;
50
RestWebServer(ControllerOpenThread & aNcp)51 RestWebServer::RestWebServer(ControllerOpenThread &aNcp)
52 : mResource(Resource(&aNcp))
53 , mListenFd(-1)
54 {
55 }
56
~RestWebServer(void)57 RestWebServer::~RestWebServer(void)
58 {
59 if (mListenFd != -1)
60 {
61 close(mListenFd);
62 }
63 }
64
Init(void)65 void RestWebServer::Init(void)
66 {
67 mResource.Init();
68 InitializeListenFd();
69 }
70
Update(MainloopContext & aMainloop)71 void RestWebServer::Update(MainloopContext &aMainloop)
72 {
73 FD_SET(mListenFd, &aMainloop.mReadFdSet);
74 aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, mListenFd);
75
76 return;
77 }
78
Process(const MainloopContext & aMainloop)79 void RestWebServer::Process(const MainloopContext &aMainloop)
80 {
81 UpdateConnections(aMainloop.mReadFdSet);
82 }
83
UpdateConnections(const fd_set & aReadFdSet)84 void RestWebServer::UpdateConnections(const fd_set &aReadFdSet)
85 {
86 otbrError error = OTBR_ERROR_NONE;
87 auto eraseIt = mConnectionSet.begin();
88
89 // Erase useless connections
90 for (eraseIt = mConnectionSet.begin(); eraseIt != mConnectionSet.end();)
91 {
92 Connection *connection = eraseIt->second.get();
93
94 if (connection->IsComplete())
95 {
96 eraseIt = mConnectionSet.erase(eraseIt);
97 }
98 else
99 {
100 eraseIt++;
101 }
102 }
103
104 // Create new connection if listenfd is set
105 if (FD_ISSET(mListenFd, &aReadFdSet) && mConnectionSet.size() < kMaxServeNum)
106 {
107 error = Accept(mListenFd);
108 }
109
110 if (error != OTBR_ERROR_NONE)
111 {
112 otbrLogWarning("Failed to accept new connection: %s", otbrErrorString(error));
113 }
114 }
115
InitializeListenFd(void)116 void RestWebServer::InitializeListenFd(void)
117 {
118 otbrError error = OTBR_ERROR_NONE;
119 std::string errorMessage;
120 int32_t ret;
121 int32_t err = errno;
122 int32_t optval = 1;
123
124 mAddress.sin_family = AF_INET;
125 mAddress.sin_addr.s_addr = INADDR_ANY;
126 mAddress.sin_port = htons(kPortNumber);
127
128 mListenFd = SocketWithCloseExec(AF_INET, SOCK_STREAM, 0, kSocketNonBlock);
129 VerifyOrExit(mListenFd != -1, err = errno, error = OTBR_ERROR_REST, errorMessage = "socket");
130
131 ret = setsockopt(mListenFd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&optval), sizeof(optval));
132 VerifyOrExit(ret == 0, err = errno, error = OTBR_ERROR_REST, errorMessage = "sock opt");
133
134 ret = bind(mListenFd, reinterpret_cast<struct sockaddr *>(&mAddress), sizeof(sockaddr));
135 VerifyOrExit(ret == 0, err = errno, error = OTBR_ERROR_REST, errorMessage = "bind");
136
137 ret = listen(mListenFd, 5);
138 VerifyOrExit(ret >= 0, err = errno, error = OTBR_ERROR_REST, errorMessage = "listen");
139
140 exit:
141
142 if (error != OTBR_ERROR_NONE)
143 {
144 otbrLogErr("InitializeListenFd error %s : %s", errorMessage.c_str(), strerror(err));
145 }
146
147 VerifyOrDie(error == OTBR_ERROR_NONE, "otbr rest server init error");
148 }
149
Accept(int aListenFd)150 otbrError RestWebServer::Accept(int aListenFd)
151 {
152 std::string errorMessage;
153 otbrError error = OTBR_ERROR_NONE;
154 int32_t err;
155 int32_t fd;
156 sockaddr_in tmp;
157 socklen_t addrlen = sizeof(tmp);
158
159 fd = accept(aListenFd, reinterpret_cast<struct sockaddr *>(&mAddress), &addrlen);
160 err = errno;
161
162 VerifyOrExit(fd >= 0, err = errno, error = OTBR_ERROR_REST, errorMessage = "accept");
163
164 VerifyOrExit(SetFdNonblocking(fd), err = errno, error = OTBR_ERROR_REST; errorMessage = "set nonblock");
165
166 CreateNewConnection(fd);
167
168 exit:
169 if (error != OTBR_ERROR_NONE)
170 {
171 if (fd != -1)
172 {
173 close(fd);
174 fd = -1;
175 }
176 otbrLogErr("Rest server accept error: %s %s", errorMessage.c_str(), strerror(err));
177 }
178
179 return error;
180 }
181
CreateNewConnection(int & aFd)182 void RestWebServer::CreateNewConnection(int &aFd)
183 {
184 auto it =
185 mConnectionSet.emplace(aFd, std::unique_ptr<Connection>(new Connection(steady_clock::now(), &mResource, aFd)));
186
187 if (it.second == true)
188 {
189 Connection *connection = it.first->second.get();
190 connection->Init();
191 }
192 else
193 {
194 // failure on inserting new connection
195 close(aFd);
196 aFd = -1;
197 }
198 }
199
SetFdNonblocking(int32_t fd)200 bool RestWebServer::SetFdNonblocking(int32_t fd)
201 {
202 int32_t oldMode;
203 bool ret = true;
204
205 oldMode = fcntl(fd, F_GETFL);
206
207 VerifyOrExit(fcntl(fd, F_SETFL, oldMode | O_NONBLOCK) >= 0, ret = false);
208
209 exit:
210 return ret;
211 }
212
213 } // namespace rest
214 } // namespace otbr
215