• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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