1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
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 "client.h"
16 #include "server.h"
17
18 namespace Hdc {
HdcClient(const bool serverOrClient,const string & addrString,uv_loop_t * loopMainIn)19 HdcClient::HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn)
20 : HdcChannelBase(serverOrClient, addrString, loopMainIn)
21 {
22 MallocChannel(&channel); // free by logic
23 debugRetryCount = 0;
24 #ifndef _WIN32
25 Base::ZeroStruct(terminalState);
26 #endif
27 }
28
~HdcClient()29 HdcClient::~HdcClient()
30 {
31 Base::TryCloseLoop(loopMain, "ExecuteCommand finish");
32 }
33
NotifyInstanceChannelFree(HChannel hChannel)34 void HdcClient::NotifyInstanceChannelFree(HChannel hChannel)
35 {
36 if (bShellInteractive) {
37 WRITE_LOG(LOG_DEBUG, "Restore tty");
38 ModifyTty(false, &hChannel->stdinTty);
39 }
40 }
41
GetLastPID()42 uint32_t HdcClient::GetLastPID()
43 {
44 char bufPath[BUF_SIZE_MEDIUM] = "";
45 size_t size = BUF_SIZE_MEDIUM;
46 char pidBuf[BUF_SIZE_TINY] = "";
47 // get running pid to kill it
48 if (uv_os_tmpdir(bufPath, &size) < 0) {
49 WRITE_LOG(LOG_FATAL, "Tmppath failed");
50 return 0;
51 }
52 string path = Base::StringFormat("%s%c.%s.pid", bufPath, Base::GetPathSep(), SERVER_NAME.c_str());
53 Base::ReadBinFile(path.c_str(), (void **)&pidBuf, BUF_SIZE_TINY);
54 int pid = atoi(pidBuf); // pid maybe 0
55 return pid;
56 }
57
StartKillServer(const char * cmd,bool startOrKill)58 bool HdcClient::StartKillServer(const char *cmd, bool startOrKill)
59 {
60 bool isNowRunning = Base::ProgramMutex(SERVER_NAME.c_str(), true) != 0;
61 const int SIGN_NUM = 9;
62 uint32_t pid = GetLastPID();
63 if (!pid) {
64 return false;
65 }
66 if (startOrKill) {
67 if (isNowRunning) {
68 // already running
69 if (!strstr(cmd, " -r")) {
70 return true;
71 }
72 if (pid) {
73 uv_kill(pid, SIGN_NUM);
74 }
75 }
76 HdcServer::PullupServer(channelHostPort.c_str());
77 } else {
78 if (isNowRunning && pid) {
79 uv_kill(pid, SIGN_NUM);
80 Base::PrintMessage("Kill server finish");
81 }
82 // already running
83 if (!strstr(cmd, " -r")) {
84 return true;
85 }
86 HdcServer::PullupServer(channelHostPort.c_str());
87 }
88 return true;
89 }
90
DoCtrlServiceWork(uv_check_t * handle)91 void HdcClient::DoCtrlServiceWork(uv_check_t *handle)
92 {
93 HdcClient *thisClass = (HdcClient *)handle->data;
94 const char *cmd = thisClass->command.c_str();
95 string &strCmd = thisClass->command;
96 while (true) {
97 if (!strncmp(cmd, CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size())) {
98 thisClass->StartKillServer(cmd, true);
99 } else if (!strncmp(cmd, CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size())) {
100 thisClass->StartKillServer(cmd, false);
101 // clang-format off
102 } else if (!strncmp(cmd, CMDSTR_GENERATE_KEY.c_str(), CMDSTR_GENERATE_KEY.size()) &&
103 strCmd.find(" ") != std::string::npos) {
104 // clang-format on
105 string keyPath = strCmd.substr(CMDSTR_GENERATE_KEY.size() + 1, strCmd.size());
106 HdcAuth::GenerateKey(keyPath.c_str());
107 } else {
108 Base::PrintMessage("Unknown command");
109 }
110 break;
111 }
112 Base::TryCloseHandle((const uv_handle_t *)handle);
113 }
114
CtrlServiceWork(const char * commandIn)115 int HdcClient::CtrlServiceWork(const char *commandIn)
116 {
117 command = commandIn;
118 ctrlServerWork.data = this;
119 uv_check_init(loopMain, &ctrlServerWork);
120 uv_check_start(&ctrlServerWork, DoCtrlServiceWork);
121 uv_run(loopMain, UV_RUN_NOWAIT);
122 return 0;
123 }
124
AutoConnectKey(string & doCommand,const string & preConnectKey) const125 string HdcClient::AutoConnectKey(string &doCommand, const string &preConnectKey) const
126 {
127 string key = preConnectKey;
128 bool isNoTargetCommand = false;
129 vector<string> vecNoConnectKeyCommand;
130 vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_VERSION);
131 vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_HELP);
132 vecNoConnectKeyCommand.push_back(CMDSTR_TARGET_DISCOVER);
133 vecNoConnectKeyCommand.push_back(CMDSTR_LIST_TARGETS);
134 vecNoConnectKeyCommand.push_back(CMDSTR_CONNECT_TARGET);
135 vecNoConnectKeyCommand.push_back(CMDSTR_KILL_SERVER);
136 vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " ls");
137 vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " rm");
138 for (string v : vecNoConnectKeyCommand) {
139 if (!doCommand.compare(0, v.size(), v)) {
140 isNoTargetCommand = true;
141 break;
142 }
143 }
144 if (isNoTargetCommand) {
145 key = "";
146 } else {
147 if (!preConnectKey.size()) {
148 key = CMDSTR_CONNECT_ANY;
149 }
150 }
151 return key;
152 }
153
ExecuteCommand(const string & commandIn)154 int HdcClient::ExecuteCommand(const string &commandIn)
155 {
156 char ip[BUF_SIZE_TINY] = "";
157 uint16_t port = 0;
158 int ret = Base::ConnectKey2IPPort(channelHostPort.c_str(), ip, &port);
159 if (ret < 0) {
160 WRITE_LOG(LOG_FATAL, "ConnectKey2IPPort %s failed with %d",
161 channelHostPort.c_str(), ret);
162 return -1;
163 }
164 command = commandIn;
165 connectKey = AutoConnectKey(command, connectKey);
166 ConnectServerForClient(ip, port);
167 uv_timer_init(loopMain, &waitTimeDoCmd);
168 waitTimeDoCmd.data = this;
169 uv_timer_start(&waitTimeDoCmd, CommandWorker, 10, 10);
170 WorkerPendding();
171 return 0;
172 }
173
Initial(const string & connectKeyIn)174 int HdcClient::Initial(const string &connectKeyIn)
175 {
176 connectKey = connectKeyIn;
177 if (!channelHostPort.size() || !channelHost.size() || !channelPort) {
178 WRITE_LOG(LOG_FATAL, "Listen string initial failed");
179 return ERR_PARM_FAIL;
180 }
181 return 0;
182 }
183
ConnectServerForClient(const char * ip,uint16_t port)184 int HdcClient::ConnectServerForClient(const char *ip, uint16_t port)
185 {
186 if (uv_is_closing((const uv_handle_t *)&channel->hWorkTCP)) {
187 return ERR_SOCKET_FAIL;
188 }
189 WRITE_LOG(LOG_DEBUG, "Try to connect %s:%d", ip, port);
190 struct sockaddr_in6 dest;
191 uv_ip6_addr(ip, port, &dest);
192 uv_connect_t *conn = new(std::nothrow) uv_connect_t();
193 if (conn == nullptr) {
194 WRITE_LOG(LOG_FATAL, "ConnectServerForClient new conn failed");
195 return ERR_GENERIC;
196 }
197 conn->data = this;
198 uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&dest, Connect);
199 return 0;
200 }
201
CommandWorker(uv_timer_t * handle)202 void HdcClient::CommandWorker(uv_timer_t *handle)
203 {
204 const uint16_t maxWaitRetry = 500;
205 HdcClient *thisClass = (HdcClient *)handle->data;
206 if (++thisClass->debugRetryCount > maxWaitRetry) {
207 uv_timer_stop(handle);
208 uv_stop(thisClass->loopMain);
209 WRITE_LOG(LOG_DEBUG, "Connect server failed");
210 fprintf(stderr, "Connect server failed\n");
211 return;
212 }
213 if (!thisClass->channel->handshakeOK) {
214 return;
215 }
216 uv_timer_stop(handle);
217 WRITE_LOG(LOG_DEBUG, "Connect server successful");
218 thisClass->Send(thisClass->channel->channelId, (uint8_t *)thisClass->command.c_str(),
219 thisClass->command.size() + 1);
220 }
221
AllocStdbuf(uv_handle_t * handle,size_t sizeWanted,uv_buf_t * buf)222 void HdcClient::AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)
223 {
224 if (sizeWanted <= 0) {
225 return;
226 }
227 HChannel context = (HChannel)handle->data;
228 int availSize = strlen(context->bufStd);
229 buf->base = (char *)context->bufStd + availSize;
230 buf->len = sizeof(context->bufStd) - availSize - 2; // reserve 2bytes
231 }
232
ReadStd(uv_stream_t * stream,ssize_t nread,const uv_buf_t * buf)233 void HdcClient::ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
234 {
235 HChannel hChannel = (HChannel)stream->data;
236 HdcClient *thisClass = (HdcClient *)hChannel->clsChannel;
237 char *command = hChannel->bufStd;
238 if (nread <= 0) {
239 return; // error
240 }
241 thisClass->Send(hChannel->channelId, (uint8_t *)command, strlen(command));
242 Base::ZeroArray(hChannel->bufStd);
243 }
244
ModifyTty(bool setOrRestore,uv_tty_t * tty)245 void HdcClient::ModifyTty(bool setOrRestore, uv_tty_t *tty)
246 {
247 if (setOrRestore) {
248 #ifdef _WIN32
249 uv_tty_set_mode(tty, UV_TTY_MODE_RAW);
250 #else
251 if (tcgetattr(STDIN_FILENO, &terminalState))
252 return;
253 termios tio;
254 if (tcgetattr(STDIN_FILENO, &tio))
255 return;
256 cfmakeraw(&tio);
257 tio.c_cc[VTIME] = 0;
258 tio.c_cc[VMIN] = 1;
259 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
260 #endif
261 } else {
262 #ifndef _WIN32
263 tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
264 #endif
265 }
266 }
267
BindLocalStd(HChannel hChannel)268 void HdcClient::BindLocalStd(HChannel hChannel)
269 {
270 if (command == CMDSTR_SHELL) {
271 bShellInteractive = true;
272 }
273 if (bShellInteractive && uv_guess_handle(STDIN_FILENO) != UV_TTY) {
274 WRITE_LOG(LOG_WARN, "Not support stdio TTY mode");
275 return;
276 }
277
278 WRITE_LOG(LOG_DEBUG, "setup stdio TTY mode");
279 if (uv_tty_init(loopMain, &hChannel->stdoutTty, STDOUT_FILENO, 0)
280 || uv_tty_init(loopMain, &hChannel->stdinTty, STDIN_FILENO, 1)) {
281 WRITE_LOG(LOG_DEBUG, "uv_tty_init failed");
282 return;
283 }
284 hChannel->stdoutTty.data = hChannel;
285 ++hChannel->uvHandleRef;
286 hChannel->stdinTty.data = hChannel;
287 ++hChannel->uvHandleRef;
288 if (bShellInteractive) {
289 WRITE_LOG(LOG_DEBUG, "uv_tty_init uv_tty_set_mode");
290 ModifyTty(true, &hChannel->stdinTty);
291 uv_read_start((uv_stream_t *)&hChannel->stdinTty, AllocStdbuf, ReadStd);
292 }
293 }
294
Connect(uv_connect_t * connection,int status)295 void HdcClient::Connect(uv_connect_t *connection, int status)
296 {
297 HdcClient *thisClass = (HdcClient *)connection->data;
298 delete connection;
299 HChannel hChannel = (HChannel)thisClass->channel;
300 if (status < 0 || uv_is_closing((const uv_handle_t *)&hChannel->hWorkTCP)) {
301 WRITE_LOG(LOG_FATAL, "connect failed");
302 thisClass->FreeChannel(hChannel->channelId);
303 return;
304 }
305 thisClass->BindLocalStd(hChannel);
306 Base::SetTcpOptions((uv_tcp_t *)&hChannel->hWorkTCP);
307 uv_read_start((uv_stream_t *)&hChannel->hWorkTCP, AllocCallback, ReadStream);
308 }
309
PreHandshake(HChannel hChannel,const uint8_t * buf)310 int HdcClient::PreHandshake(HChannel hChannel, const uint8_t *buf)
311 {
312 ChannelHandShake *hShake = (ChannelHandShake *)buf;
313 if (strncmp(hShake->banner, HANDSHAKE_MESSAGE.c_str(), HANDSHAKE_MESSAGE.size())) {
314 hChannel->availTailIndex = 0;
315 WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
316 return ERR_BUF_CHECK;
317 }
318 // sync remote session id to local
319 uint32_t unOld = hChannel->channelId;
320 hChannel->channelId = ntohl(hShake->channelId);
321 AdminChannel(OP_UPDATE, unOld, hChannel);
322 WRITE_LOG(LOG_DEBUG, "Client channel handshake finished, use connectkey:%s", connectKey.c_str());
323 // send config
324 // channel handshake step2
325 if (memset_s(hShake->connectKey, sizeof(hShake->connectKey), 0, sizeof(hShake->connectKey)) != EOK
326 || memcpy_s(hShake->connectKey, sizeof(hShake->connectKey), connectKey.c_str(), connectKey.size()) != EOK) {
327 hChannel->availTailIndex = 0;
328 WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
329 return ERR_BUF_COPY;
330 }
331 Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), sizeof(ChannelHandShake));
332 hChannel->handshakeOK = true;
333 #ifdef HDC_CHANNEL_KEEP_ALIVE
334 // Evaluation method, non long-term support
335 Send(hChannel->channelId, reinterpret_cast<uint8_t *>(const_cast<char*>(CMDSTR_INNER_ENABLE_KEEPALIVE.c_str())),
336 CMDSTR_INNER_ENABLE_KEEPALIVE.size());
337 #endif
338 return RET_SUCCESS;
339 }
340
341 // read serverForClient(server)TCP data
ReadChannel(HChannel hChannel,uint8_t * buf,const int bytesIO)342 int HdcClient::ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)
343 {
344 if (!hChannel->handshakeOK) {
345 return PreHandshake(hChannel, buf);
346 }
347 #ifdef UNIT_TEST
348 // Do not output to console when the unit test
349 return 0;
350 #endif
351 WRITE_LOG(LOG_DEBUG, "Client ReadChannel :%d", bytesIO);
352 string s(reinterpret_cast<char *>(buf), bytesIO);
353 fprintf(stdout, "%s", s.c_str());
354 fflush(stdout);
355 return 0;
356 };
357 } // namespace Hdc
358