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