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 #ifndef TEST_HASH
17 #include "hdc_hash_gen.h"
18 #endif
19 #include "host_updater.h"
20 #include "server.h"
21 #include "file.h"
22 
23 std::map<std::string, std::string> g_lists;
24 bool g_show = true;
25 
26 namespace Hdc {
27 bool g_terminalStateChange = false;
HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn, bool checkVersion)28 HdcClient::HdcClient(const bool serverOrClient, const string &addrString, uv_loop_t *loopMainIn, bool checkVersion)
29     : HdcChannelBase(serverOrClient, addrString, loopMainIn)
30 {
31     MallocChannel(&channel);  // free by logic
32     debugRetryCount = 0;
33 #ifndef _WIN32
34     Base::ZeroStruct(terminalState);
35 #endif
36     isCheckVersionCmd = checkVersion;
37 }
38 
~HdcClient()39 HdcClient::~HdcClient()
40 {
41 #ifndef _WIN32
42     if (g_terminalStateChange) {
43         tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
44     }
45 #endif
46     Base::TryCloseLoop(loopMain, "ExecuteCommand finish");
47 }
48 
NotifyInstanceChannelFree(HChannel hChannel)49 void HdcClient::NotifyInstanceChannelFree(HChannel hChannel)
50 {
51     if (bShellInteractive) {
52         WRITE_LOG(LOG_DEBUG, "Restore tty");
53         ModifyTty(false, &hChannel->stdinTty);
54     }
55 }
56 
GetLastPID()57 uint32_t HdcClient::GetLastPID()
58 {
59     char bufPath[BUF_SIZE_MEDIUM] = "";
60     size_t size = BUF_SIZE_MEDIUM;
61     char pidBuf[BUF_SIZE_TINY] = "";
62     // get running pid to kill it
63     if (uv_os_tmpdir(bufPath, &size) < 0) {
64         WRITE_LOG(LOG_FATAL, "Tmppath failed");
65         return 0;
66     }
67     string path = Base::StringFormat("%s%c.%s.pid", bufPath, Base::GetPathSep(), SERVER_NAME.c_str());
68     Base::ReadBinFile(path.c_str(), reinterpret_cast<void **>(&pidBuf), BUF_SIZE_TINY);
69     int pid = atoi(pidBuf);  // pid  maybe 0
70     return pid;
71 }
72 
StartServer(const string &cmd)73 bool HdcClient::StartServer(const string &cmd)
74 {
75     int serverStatus = Base::ProgramMutex(SERVER_NAME.c_str(), true);
76     if (serverStatus < 0) {
77         WRITE_LOG(LOG_DEBUG, "get server status failed, serverStatus:%d", serverStatus);
78         return false;
79     }
80 
81     // server is not running
82     if (serverStatus == 0) {
83         HdcServer::PullupServer(channelHostPort.c_str());
84         return true;
85     }
86 
87     // server is running
88     if (cmd.find(" -r") == std::string::npos) {
89         return true;
90     }
91 
92     // restart server
93     uint32_t pid = GetLastPID();
94     if (pid == 0) {
95         Base::PrintMessage(TERMINAL_HDC_PROCESS_FAILED.c_str());
96         return false;
97     }
98     int rc = uv_kill(pid, SIGKILL);
99     WRITE_LOG(LOG_DEBUG, "uv_kill rc:%d", rc);
100     HdcServer::PullupServer(channelHostPort.c_str());
101     return true;
102 }
103 
KillServer(const string &cmd)104 bool HdcClient::KillServer(const string &cmd)
105 {
106     int serverStatus = Base::ProgramMutex(SERVER_NAME.c_str(), true);
107     if (serverStatus < 0) {
108         WRITE_LOG(LOG_DEBUG, "get server status failed, serverStatus:%d", serverStatus);
109         return false;
110     }
111 
112     // server is not running
113     if (serverStatus == 0) {
114         if (cmd.find(" -r") != std::string::npos) {
115             HdcServer::PullupServer(channelHostPort.c_str());
116         }
117         return true;
118     }
119 
120     // server is running
121     uint32_t pid = GetLastPID();
122     if (pid == 0) {
123         Base::PrintMessage(TERMINAL_HDC_PROCESS_FAILED.c_str());
124         return false;
125     }
126     int rc = uv_kill(pid, SIGKILL);
127     if (rc == 0) {
128         Base::PrintMessage("Kill server finish");
129     } else {
130         constexpr int size = 1024;
131         char buf[size] = { 0 };
132         uv_strerror_r(rc, buf, size);
133         Base::PrintMessage("Kill server failed %s", buf);
134     }
135     if (cmd.find(" -r") != std::string::npos) {
136         HdcServer::PullupServer(channelHostPort.c_str());
137     }
138     return true;
139 }
140 
DoCtrlServiceWork(uv_check_t *handle)141 void HdcClient::DoCtrlServiceWork(uv_check_t *handle)
142 {
143     HdcClient *thisClass = (HdcClient *)handle->data;
144     string &strCmd = thisClass->command;
145     if (!strncmp(thisClass->command.c_str(), CMDSTR_SERVICE_START.c_str(), CMDSTR_SERVICE_START.size())) {
146         thisClass->StartServer(strCmd);
147     } else if (!strncmp(thisClass->command.c_str(), CMDSTR_SERVICE_KILL.c_str(), CMDSTR_SERVICE_KILL.size())) {
148         thisClass->KillServer(strCmd);
149         // clang-format off
150     } else if (!strncmp(thisClass->command.c_str(), CMDSTR_GENERATE_KEY.c_str(), CMDSTR_GENERATE_KEY.size()) &&
151                 strCmd.find(" ") != std::string::npos) {
152         // clang-format on
153         string keyPath = strCmd.substr(CMDSTR_GENERATE_KEY.size() + 1, strCmd.size());
154         HdcAuth::GenerateKey(keyPath.c_str());
155     } else {
156         Base::PrintMessage("Unknown command");
157     }
158     Base::TryCloseHandle((const uv_handle_t *)handle);
159 }
160 
CtrlServiceWork(const char *commandIn)161 int HdcClient::CtrlServiceWork(const char *commandIn)
162 {
163     command = commandIn;
164     ctrlServerWork.data = this;
165     uv_check_init(loopMain, &ctrlServerWork);
166     uv_check_start(&ctrlServerWork, DoCtrlServiceWork);
167     uv_run(loopMain, UV_RUN_NOWAIT);
168     return 0;
169 }
170 
AutoConnectKey(string &doCommand, const string &preConnectKey) const171 string HdcClient::AutoConnectKey(string &doCommand, const string &preConnectKey) const
172 {
173     string key = preConnectKey;
174     bool isNoTargetCommand = false;
175     vector<string> vecNoConnectKeyCommand;
176     vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_VERSION);
177     vecNoConnectKeyCommand.push_back(CMDSTR_SOFTWARE_HELP);
178     vecNoConnectKeyCommand.push_back(CMDSTR_TARGET_DISCOVER);
179     vecNoConnectKeyCommand.push_back(CMDSTR_LIST_TARGETS);
180     vecNoConnectKeyCommand.push_back(CMDSTR_CHECK_SERVER);
181     vecNoConnectKeyCommand.push_back(CMDSTR_CONNECT_TARGET);
182     vecNoConnectKeyCommand.push_back(CMDSTR_CHECK_DEVICE);
183     vecNoConnectKeyCommand.push_back(CMDSTR_WAIT_FOR);
184     vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " ls");
185     vecNoConnectKeyCommand.push_back(CMDSTR_FORWARD_FPORT + " rm");
186     for (string v : vecNoConnectKeyCommand) {
187         if (!doCommand.compare(0, v.size(), v)) {
188             isNoTargetCommand = true;
189             break;
190         }
191     }
192     if (isNoTargetCommand) {
193         if (this->command != CMDSTR_WAIT_FOR) {
194             key = "";
195         }
196     } else {
197         if (!preConnectKey.size()) {
198             key = CMDSTR_CONNECT_ANY;
199         }
200     }
201     return key;
202 }
203 
204 #ifdef _WIN32
ReadFileThreadFunc(void* arg)205 static void ReadFileThreadFunc(void* arg)
206 {
207     char buffer[BUF_SIZE_DEFAULT] = { 0 };
208     DWORD bytesRead = 0;
209 
210     HANDLE* read = reinterpret_cast<HANDLE*>(arg);
211     while (true) {
212         if (!ReadFile(*read, buffer, BUF_SIZE_DEFAULT - 1, &bytesRead, NULL)) {
213             break;
214         }
215         string str = std::to_string(bytesRead);
216         const char* zero = "0";
217         if (!strncmp(zero, str.c_str(), strlen(zero))) {
218             return;
219         }
220         printf("%s", buffer);
221         if (memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)) != EOK) {
222             return;
223         }
224     }
225 }
226 
GetHilogPath()227 string HdcClient::GetHilogPath()
228 {
229     string hdcPath = Base::GetHdcAbsolutePath();
230     int index = hdcPath.find_last_of(Base::GetPathSep());
231     string exePath = hdcPath.substr(0, index) + Base::GetPathSep() + HILOG_NAME;
232 
233     return exePath;
234 }
235 
RunCommandWin32(const string& cmd)236 void HdcClient::RunCommandWin32(const string& cmd)
237 {
238     HANDLE hSubWrite;
239     HANDLE hParentRead;
240     HANDLE hParentWrite;
241     HANDLE hSubRead;
242     STARTUPINFO si;
243     PROCESS_INFORMATION pi;
244     SECURITY_ATTRIBUTES sa;
245     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
246     sa.lpSecurityDescriptor = NULL;
247     sa.bInheritHandle = true;
248 
249     if (!CreatePipe(&hParentRead, &hSubWrite, &sa, 0) ||
250         !CreatePipe(&hSubRead, &hParentWrite, &sa, 0) ||
251         !SetHandleInformation(hParentRead, HANDLE_FLAG_INHERIT, 0) ||
252         !SetHandleInformation(hParentWrite, HANDLE_FLAG_INHERIT, 0)) {
253         return;
254     }
255 
256     ZeroMemory(&si, sizeof(STARTUPINFO));
257     si.cb = sizeof(STARTUPINFO);
258     GetStartupInfo(&si);
259     si.hStdError = hSubWrite;
260     si.hStdOutput = hSubWrite;
261     si.hStdInput = hSubRead;
262     si.wShowWindow = SW_HIDE;
263     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
264 
265     const char *msg = cmd.c_str();
266     char buffer[BUF_SIZE_SMALL] = {0};
267     if (strcpy_s(buffer, sizeof(buffer), msg) != EOK) {
268         return;
269     }
270     const string exePath = GetHilogPath();
271     if (!CreateProcess(_T(exePath.c_str()), _T(buffer), NULL, NULL, true, NULL, NULL, NULL, &si, &pi)) {
272         WRITE_LOG(LOG_INFO, "create process failed, error:%d", GetLastError());
273         return;
274     }
275     auto thread = std::thread([&hParentRead]() {
276         ReadFileThreadFunc(&hParentRead);
277     });
278     WaitForSingleObject(pi.hProcess, INFINITE);
279     CloseHandle(hSubWrite);
280     CloseHandle(pi.hProcess);
281     CloseHandle(pi.hThread);
282     thread.join();
283     CloseHandle(hParentRead);
284     CloseHandle(hParentWrite);
285     CloseHandle(hSubRead);
286 }
287 #else
RunCommand(const string& cmd)288 void HdcClient::RunCommand(const string& cmd)
289 {
290     FILE *procFileInfo = nullptr;
291     procFileInfo = popen(cmd.c_str(), "r");
292     if (procFileInfo == nullptr) {
293         perror("popen execute failed");
294         return;
295     }
296     char resultBufShell[BUF_SIZE_DEFAULT] = {0};
297     while (fgets(resultBufShell, sizeof(resultBufShell), procFileInfo) != nullptr) {
298         printf("%s", resultBufShell);
299         if (memset_s(resultBufShell, sizeof(resultBufShell), 0, sizeof(resultBufShell)) != EOK) {
300             break;
301         }
302     }
303     pclose(procFileInfo);
304 }
305 #endif
306 
RunExecuteCommand(const string& cmd)307 void HdcClient::RunExecuteCommand(const string& cmd)
308 {
309 #ifdef _WIN32
310     RunCommandWin32(cmd);
311 #else
312     RunCommand(cmd);
313 #endif
314 }
315 
IsCaptureCommand(const string& cmd)316 bool IsCaptureCommand(const string& cmd)
317 {
318     int index = string(CMDSTR_HILOG).length();
319     int length = cmd.length();
320     const string captureOption = "parse";
321     while (index < length) {
322         if (cmd[index] == ' ') {
323             index++;
324             continue;
325         }
326         if (!strncmp(cmd.c_str() + index, captureOption.c_str(), captureOption.size())) {
327             return true;
328         } else {
329             return false;
330         }
331     }
332     return false;
333 }
334 
ExecuteCommand(const string &commandIn)335 int HdcClient::ExecuteCommand(const string &commandIn)
336 {
337     char ip[BUF_SIZE_TINY] = "";
338     uint16_t port = 0;
339     int ret = Base::ConnectKey2IPPort(channelHostPort.c_str(), ip, &port, sizeof(ip));
340     if (ret < 0) {
341         WRITE_LOG(LOG_FATAL, "ConnectKey2IPPort %s failed with %d",
342                   channelHostPort.c_str(), ret);
343         return -1;
344     }
345 
346     if (!strncmp(commandIn.c_str(), CMDSTR_HILOG.c_str(), CMDSTR_HILOG.size()) &&
347         IsCaptureCommand(commandIn)) {
348         RunExecuteCommand(commandIn);
349         return 0;
350     }
351 
352     if (!strncmp(commandIn.c_str(), CMDSTR_FILE_SEND.c_str(), CMDSTR_FILE_SEND.size()) ||
353         !strncmp(commandIn.c_str(), CMDSTR_FILE_RECV.c_str(), CMDSTR_FILE_RECV.size())) {
354         WRITE_LOG(LOG_DEBUG, "Set file send mode");
355         channel->remote = RemoteType::REMOTE_FILE;
356     }
357     if (!strncmp(commandIn.c_str(), CMDSTR_APP_INSTALL.c_str(), CMDSTR_APP_INSTALL.size())) {
358         channel->remote = RemoteType::REMOTE_APP;
359     }
360     command = commandIn;
361     connectKey = AutoConnectKey(command, connectKey);
362     ConnectServerForClient(ip, port);
363     uv_timer_init(loopMain, &waitTimeDoCmd);
364     waitTimeDoCmd.data = this;
365     uv_timer_start(&waitTimeDoCmd, CommandWorker, UV_START_TIMEOUT, UV_START_REPEAT);
366     WorkerPendding();
367     return 0;
368 }
369 
Initial(const string &connectKeyIn)370 int HdcClient::Initial(const string &connectKeyIn)
371 {
372     connectKey = connectKeyIn;
373     if (!channelHostPort.size() || !channelHost.size() || !channelPort) {
374         WRITE_LOG(LOG_FATAL, "Listen string initial failed");
375         return ERR_PARM_FAIL;
376     }
377     return 0;
378 }
379 
ConnectServerForClient(const char *ip, uint16_t port)380 int HdcClient::ConnectServerForClient(const char *ip, uint16_t port)
381 {
382     if (uv_is_closing((const uv_handle_t *)&channel->hWorkTCP)) {
383         WRITE_LOG(LOG_FATAL, "ConnectServerForClient uv_is_closing");
384         return ERR_SOCKET_FAIL;
385     }
386     WRITE_LOG(LOG_DEBUG, "Try to connect %s:%d", ip, port);
387     uv_connect_t *conn = new(std::nothrow) uv_connect_t();
388     if (conn == nullptr) {
389         WRITE_LOG(LOG_FATAL, "ConnectServerForClient new conn failed");
390         return ERR_GENERIC;
391     }
392     conn->data = this;
393     tcpConnectRetryCount = 0;
394     uv_timer_init(loopMain, &retryTcpConnTimer);
395     retryTcpConnTimer.data = this;
396     if (strchr(ip, '.')) {
397         isIpV4 = true;
398         std::string s = ip;
399         size_t index = s.find(IPV4_MAPPING_PREFIX);
400         size_t size = IPV4_MAPPING_PREFIX.size();
401         if (index != std::string::npos) {
402             s = s.substr(index + size);
403         }
404         WRITE_LOG(LOG_DEBUG, "ConnectServerForClient ipv4 %s:%d", s.c_str(), port);
405         uv_ip4_addr(s.c_str(), port, &destv4);
406         uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&destv4, Connect);
407     } else {
408         isIpV4 = false;
409         WRITE_LOG(LOG_DEBUG, "ConnectServerForClient ipv6 %s:%d", ip, port);
410         uv_ip6_addr(ip, port, &dest);
411         uv_tcp_connect(conn, (uv_tcp_t *)&channel->hWorkTCP, (const struct sockaddr *)&dest, Connect);
412     }
413     return 0;
414 }
415 
CommandWorker(uv_timer_t *handle)416 void HdcClient::CommandWorker(uv_timer_t *handle)
417 {
418     const uint16_t maxWaitRetry = 1200; // client socket try 12s
419     HdcClient *thisClass = (HdcClient *)handle->data;
420     if (++thisClass->debugRetryCount > maxWaitRetry) {
421         uv_timer_stop(handle);
422         uv_stop(thisClass->loopMain);
423         WRITE_LOG(LOG_DEBUG, "Connect server failed");
424         fprintf(stderr, "Connect server failed\n");
425         return;
426     }
427     if (!thisClass->channel->handshakeOK) {
428         return;
429     }
430     uv_timer_stop(handle);
431     WRITE_LOG(LOG_DEBUG, "Connect server successful");
432     bool closeInput = false;
433     if (!HostUpdater::ConfirmCommand(thisClass->command, closeInput)) {
434         uv_timer_stop(handle);
435         uv_stop(thisClass->loopMain);
436         WRITE_LOG(LOG_DEBUG, "Cmd \'%s\' has been canceld", thisClass->command.c_str());
437         return;
438     }
439     while (closeInput) {
440 #ifndef _WIN32
441         if (tcgetattr(STDIN_FILENO, &thisClass->terminalState)) {
442             break;
443         }
444         termios tio;
445         if (tcgetattr(STDIN_FILENO, &tio)) {
446             break;
447         }
448         cfmakeraw(&tio);
449         tio.c_cc[VTIME] = 0;
450         tio.c_cc[VMIN] = 1;
451         tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
452         g_terminalStateChange = true;
453 #endif
454         break;
455     }
456     thisClass->Send(thisClass->channel->channelId,
457                     const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(thisClass->command.c_str())),
458                     thisClass->command.size() + 1);
459 }
460 
AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)461 void HdcClient::AllocStdbuf(uv_handle_t *handle, size_t sizeWanted, uv_buf_t *buf)
462 {
463     if (sizeWanted <= 0) {
464         return;
465     }
466     HChannel context = (HChannel)handle->data;
467     int availSize = strlen(context->bufStd);
468     buf->base = (char *)context->bufStd + availSize;
469     buf->len = sizeof(context->bufStd) - availSize - 2;  // reserve 2bytes
470 }
471 
ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)472 void HdcClient::ReadStd(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
473 {
474     HChannel hChannel = (HChannel)stream->data;
475     HdcClient *thisClass = (HdcClient *)hChannel->clsChannel;
476     char *cmd = hChannel->bufStd;
477     if (nread <= 0) {
478         WRITE_LOG(LOG_FATAL, "ReadStd error nread:%zd", nread);
479         return;  // error
480     }
481     thisClass->Send(hChannel->channelId, reinterpret_cast<uint8_t *>(cmd), strlen(cmd));
482     Base::ZeroArray(hChannel->bufStd);
483 }
484 
ModifyTty(bool setOrRestore, uv_tty_t *tty)485 void HdcClient::ModifyTty(bool setOrRestore, uv_tty_t *tty)
486 {
487     if (setOrRestore) {
488 #ifdef _WIN32
489         uv_tty_set_mode(tty, UV_TTY_MODE_RAW);
490 #else
491         if (tcgetattr(STDIN_FILENO, &terminalState)) {
492             return;
493         }
494         termios tio;
495         if (tcgetattr(STDIN_FILENO, &tio)) {
496             return;
497         }
498         cfmakeraw(&tio);
499         tio.c_cc[VTIME] = 0;
500         tio.c_cc[VMIN] = 1;
501         tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
502 #endif
503     } else {
504 #ifndef _WIN32
505         tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminalState);
506 #endif
507     }
508 }
509 
BindLocalStd(HChannel hChannel)510 void HdcClient::BindLocalStd(HChannel hChannel)
511 {
512     if (command == CMDSTR_SHELL) {
513         bShellInteractive = true;
514     }
515     if (bShellInteractive && uv_guess_handle(STDIN_FILENO) != UV_TTY) {
516         WRITE_LOG(LOG_WARN, "Not support stdio TTY mode");
517         return;
518     }
519 
520     WRITE_LOG(LOG_DEBUG, "setup stdio TTY mode");
521     if (uv_tty_init(loopMain, &hChannel->stdoutTty, STDOUT_FILENO, 0)
522         || uv_tty_init(loopMain, &hChannel->stdinTty, STDIN_FILENO, 1)) {
523         WRITE_LOG(LOG_DEBUG, "uv_tty_init failed");
524         return;
525     }
526     hChannel->stdoutTty.data = hChannel;
527     ++hChannel->uvHandleRef;
528     hChannel->stdinTty.data = hChannel;
529     ++hChannel->uvHandleRef;
530     if (bShellInteractive) {
531         WRITE_LOG(LOG_DEBUG, "uv_tty_init uv_tty_set_mode");
532         ModifyTty(true, &hChannel->stdinTty);
533         uv_read_start((uv_stream_t *)&hChannel->stdinTty, AllocStdbuf, ReadStd);
534     }
535 }
536 
Connect(uv_connect_t *connection, int status)537 void HdcClient::Connect(uv_connect_t *connection, int status)
538 {
539     WRITE_LOG(LOG_DEBUG, "Enter Connect, status:%d", status);
540     HdcClient *thisClass = (HdcClient *)connection->data;
541     delete connection;
542     HChannel hChannel = reinterpret_cast<HChannel>(thisClass->channel);
543     if (uv_is_closing((const uv_handle_t *)&hChannel->hWorkTCP)) {
544         WRITE_LOG(LOG_DEBUG, "uv_is_closing...");
545         thisClass->FreeChannel(hChannel->channelId);
546         return;
547     }
548 
549     // connect success
550     if (status == 0) {
551         thisClass->BindLocalStd(hChannel);
552         Base::SetTcpOptions((uv_tcp_t *)&hChannel->hWorkTCP);
553         WRITE_LOG(LOG_DEBUG, "uv_read_start");
554         uv_read_start((uv_stream_t *)&hChannel->hWorkTCP, AllocCallback, ReadStream);
555         return;
556     }
557 
558     // connect failed, start timer and retry
559     WRITE_LOG(LOG_DEBUG, "retry count:%d", thisClass->tcpConnectRetryCount);
560     if (thisClass->tcpConnectRetryCount >= TCP_CONNECT_MAX_RETRY_COUNT) {
561         WRITE_LOG(LOG_DEBUG, "stop retry for connect");
562         thisClass->FreeChannel(hChannel->channelId);
563         return;
564     }
565     thisClass->tcpConnectRetryCount++;
566     uv_timer_start(&(thisClass->retryTcpConnTimer), thisClass->RetryTcpConnectWorker, TCP_CONNECT_RETRY_TIME_MS, 0);
567 }
568 
RetryTcpConnectWorker(uv_timer_t *handle)569 void HdcClient::RetryTcpConnectWorker(uv_timer_t *handle)
570 {
571     HdcClient *thisClass = (HdcClient *)handle->data;
572     HChannel hChannel = reinterpret_cast<HChannel>(thisClass->channel);
573     uv_connect_t *connection = new(std::nothrow) uv_connect_t();
574     if (connection == nullptr) {
575         WRITE_LOG(LOG_FATAL, "RetryTcpConnectWorker new conn failed");
576         thisClass->FreeChannel(hChannel->channelId);
577         return;
578     }
579     connection->data = thisClass;
580     WRITE_LOG(LOG_DEBUG, "RetryTcpConnectWorker start tcp connect");
581     if (thisClass->isIpV4) {
582         uv_tcp_connect(connection, &(thisClass->channel->hWorkTCP),
583             (const struct sockaddr *)&(thisClass->destv4), thisClass->Connect);
584     } else {
585         uv_tcp_connect(connection, &(thisClass->channel->hWorkTCP),
586             (const struct sockaddr *)&(thisClass->dest), thisClass->Connect);
587     }
588 }
589 
PreHandshake(HChannel hChannel, const uint8_t *buf)590 int HdcClient::PreHandshake(HChannel hChannel, const uint8_t *buf)
591 {
592     ChannelHandShake *hShake = reinterpret_cast<ChannelHandShake *>(const_cast<uint8_t *>(buf));
593     if (strncmp(hShake->banner, HANDSHAKE_MESSAGE.c_str(), HANDSHAKE_MESSAGE.size())) {
594         hChannel->availTailIndex = 0;
595         WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
596         return ERR_BUF_CHECK;
597     }
598     hChannel->isStableBuf = (hShake->banner[BANNER_FEATURE_TAG_OFFSET] != HUGE_BUF_TAG);
599     WRITE_LOG(LOG_DEBUG, "Channel PreHandshake isStableBuf:%d", hChannel->isStableBuf);
600     if (this->command == CMDSTR_WAIT_FOR && !connectKey.empty()) {
601         hShake->banner[WAIT_TAG_OFFSET] = WAIT_DEVICE_TAG;
602     }
603     // sync remote session id to local
604     uint32_t unOld = hChannel->channelId;
605     hChannel->channelId = ntohl(hShake->channelId);
606     AdminChannel(OP_UPDATE, unOld, hChannel);
607     WRITE_LOG(LOG_DEBUG, "Client channel handshake finished, use connectkey:%s",
608               Hdc::MaskString(connectKey).c_str());
609     // send config
610     // channel handshake step2
611     if (memset_s(hShake->connectKey, sizeof(hShake->connectKey), 0, sizeof(hShake->connectKey)) != EOK
612         || memcpy_s(hShake->connectKey, sizeof(hShake->connectKey), connectKey.c_str(), connectKey.size()) != EOK) {
613         hChannel->availTailIndex = 0;
614         WRITE_LOG(LOG_DEBUG, "Channel Hello failed");
615         return ERR_BUF_COPY;
616     }
617 
618 #ifdef HDC_VERSION_CHECK
619     // add check version
620     if (!isCheckVersionCmd) { // do not check version cause user want to get server version
621         string clientVer = Base::GetVersion() + HDC_MSG_HASH;
622         string serverVer(hShake->version);
623 
624         if (clientVer != serverVer) {
625             serverVer = serverVer.substr(0, Base::GetVersion().size());
626             WRITE_LOG(LOG_FATAL, "Client version:%s, server version:%s", clientVer.c_str(), serverVer.c_str());
627             hChannel->availTailIndex = 0;
628             return ERR_CHECK_VERSION;
629         }
630     }
631     Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), sizeof(ChannelHandShake));
632 #else
633         // do not send version message if check feature disable
634     Send(hChannel->channelId, reinterpret_cast<uint8_t *>(hShake), offsetof(struct ChannelHandShake, version));
635 #endif
636     hChannel->handshakeOK = true;
637 #ifdef HDC_CHANNEL_KEEP_ALIVE
638     // Evaluation method, non long-term support
639     Send(hChannel->channelId,
640          reinterpret_cast<uint8_t *>(const_cast<char*>(CMDSTR_INNER_ENABLE_KEEPALIVE.c_str())),
641          CMDSTR_INNER_ENABLE_KEEPALIVE.size());
642 #endif
643     return RET_SUCCESS;
644 }
645 
646 // read serverForClient(server)TCP data
ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)647 int HdcClient::ReadChannel(HChannel hChannel, uint8_t *buf, const int bytesIO)
648 {
649     if (!hChannel->handshakeOK) {
650         return PreHandshake(hChannel, buf);
651     }
652 #ifdef UNIT_TEST
653     // Do not output to console when the unit test
654     return 0;
655 #endif
656     WRITE_LOG(LOG_DEBUG, "Client ReadChannel :%d", bytesIO);
657 
658     uint16_t cmd = 0;
659     bool bOffset = false;
660     if (bytesIO >= static_cast<int>(sizeof(uint16_t))) {
661         cmd = *reinterpret_cast<uint16_t *>(buf);
662         bOffset = IsOffset(cmd);
663     }
664     if (cmd == CMD_CHECK_SERVER && isCheckVersionCmd) {
665         WRITE_LOG(LOG_DEBUG, "recieve CMD_CHECK_VERSION command");
666         string version(reinterpret_cast<char *>(buf + sizeof(uint16_t)), bytesIO - sizeof(uint16_t));
667         fprintf(stdout, "Client version:%s, server version:%s\n", Base::GetVersion().c_str(), version.c_str());
668         fflush(stdout);
669         return 0;
670     }
671     if (hChannel->remote > RemoteType::REMOTE_NONE && bOffset) {
672         // file command
673         if (hChannel->remote == RemoteType::REMOTE_FILE) {
674             if (fileTask == nullptr) {
675                 HTaskInfo hTaskInfo = GetRemoteTaskInfo(hChannel);
676                 hTaskInfo->masterSlave = (cmd == CMD_FILE_INIT);
677                 fileTask = std::make_unique<HdcFile>(hTaskInfo);
678             }
679             if (!fileTask->CommandDispatch(cmd, buf + sizeof(uint16_t), bytesIO - sizeof(uint16_t))) {
680                 fileTask->TaskFinish();
681             }
682         }
683         // app command
684         if (hChannel->remote == RemoteType::REMOTE_APP) {
685             if (appTask == nullptr) {
686                 HTaskInfo hTaskInfo = GetRemoteTaskInfo(hChannel);
687                 hTaskInfo->masterSlave = (cmd == CMD_APP_INIT);
688                 appTask = std::make_unique<HdcHostApp>(hTaskInfo);
689             }
690             if (!appTask->CommandDispatch(cmd, buf + sizeof(uint16_t), bytesIO - sizeof(uint16_t))) {
691                 appTask->TaskFinish();
692             }
693         }
694         return 0;
695     }
696 
697     string s(reinterpret_cast<char *>(buf), bytesIO);
698     if (WaitFor(s)) {
699         return 0;
700     }
701     s = ListTargetsAll(s);
702     if (g_show) {
703 #ifdef _WIN32
704         fprintf(stdout, "%s", s.c_str());
705         fflush(stdout);
706 #else
707         constexpr int len = 512;
708         int size = s.size() / len;
709         int left = s.size() % len;
710         for (int i = 0; i <= size; i++) {
711             int cnt = len;
712             const char *p = reinterpret_cast<char *>(buf) + i * cnt;
713             if (i == size) {
714                 cnt = left;
715             }
716             fprintf(stdout, "%.*s", cnt, p);
717             fflush(stdout);
718             std::this_thread::sleep_for(std::chrono::milliseconds(1));
719         }
720 #endif
721     }
722     return 0;
723 }
724 
WaitFor(const string &str)725 bool HdcClient::WaitFor(const string &str)
726 {
727     bool wait = false;
728     if (!strncmp(this->command.c_str(), CMDSTR_WAIT_FOR.c_str(), CMDSTR_WAIT_FOR.size())) {
729         const string waitFor = "[Fail]No any connected target";
730         if (!strncmp(str.c_str(), waitFor.c_str(), waitFor.size())) {
731             Send(this->channel->channelId, reinterpret_cast<uint8_t *>(const_cast<char *>(this->command.c_str())),
732                  this->command.size() + 1);
733             constexpr int timeout = 1;
734             std::this_thread::sleep_for(std::chrono::seconds(timeout));
735             wait = true;
736         } else {
737             _exit(0);
738         }
739     }
740     return wait;
741 }
742 
ListTargetsAll(const string &str)743 string HdcClient::ListTargetsAll(const string &str)
744 {
745     string all = str;
746     const string lists = "list targets -v";
747     if (!strncmp(this->command.c_str(), lists.c_str(), lists.size())) {
748         UpdateList(str);
749         all = Base::ReplaceAll(all, "\n", "\thdc\n");
750     } else if (!strncmp(this->command.c_str(), CMDSTR_LIST_TARGETS.c_str(), CMDSTR_LIST_TARGETS.size())) {
751         UpdateList(str);
752     }
753     if (!strncmp(this->command.c_str(), CMDSTR_LIST_TARGETS.c_str(), CMDSTR_LIST_TARGETS.size())) {
754         if (g_lists.size() > 0 && !strncmp(str.c_str(), EMPTY_ECHO.c_str(), EMPTY_ECHO.size())) {
755             all = "";
756         }
757     }
758     return all;
759 }
760 
UpdateList(const string &str)761 void HdcClient::UpdateList(const string &str)
762 {
763     if (!strncmp(str.c_str(), EMPTY_ECHO.c_str(), EMPTY_ECHO.size())) {
764         return;
765     }
766     vector<string> devs;
767     Base::SplitString(str, "\n", devs);
768     for (size_t i = 0; i < devs.size(); i++) {
769         string::size_type pos = devs[i].find("\t");
770         if (pos != string::npos) {
771             string key = devs[i].substr(0, pos);
772             g_lists[key] = "hdc";
773         } else {
774             string key = devs[i];
775             g_lists[key] = "hdc";
776         }
777     }
778 }
779 
IsOffset(uint16_t cmd)780 bool HdcClient::IsOffset(uint16_t cmd)
781 {
782     return (cmd == CMD_CHECK_SERVER) ||
783            (cmd == CMD_FILE_INIT) ||
784            (cmd == CMD_FILE_CHECK) ||
785            (cmd == CMD_FILE_BEGIN) ||
786            (cmd == CMD_FILE_DATA) ||
787            (cmd == CMD_FILE_FINISH) ||
788            (cmd == CMD_FILE_MODE) ||
789            (cmd == CMD_DIR_MODE) ||
790            (cmd == CMD_APP_INIT) ||
791            (cmd == CMD_APP_CHECK) ||
792            (cmd == CMD_APP_BEGIN) ||
793            (cmd == CMD_APP_DATA) ||
794            (cmd == CMD_APP_FINISH);
795 }
796 
GetRemoteTaskInfo(HChannel hChannel)797 HTaskInfo HdcClient::GetRemoteTaskInfo(HChannel hChannel)
798 {
799     HTaskInfo hTaskInfo = new TaskInformation();
800     hTaskInfo->channelId = hChannel->channelId;
801     hTaskInfo->runLoop = loopMain;
802     hTaskInfo->serverOrDaemon = true;
803     hTaskInfo->channelTask = true;
804     hTaskInfo->channelClass = this;
805     hTaskInfo->isStableBuf = hChannel->isStableBuf;
806     hTaskInfo->isCleared = false;
807     return hTaskInfo;
808 };
809 }  // namespace Hdc
810