• 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 "server.h"
16 #include "host_updater.h"
17 
18 
19 namespace Hdc {
HdcServer(bool serverOrDaemonIn)20 HdcServer::HdcServer(bool serverOrDaemonIn)
21     : HdcSessionBase(serverOrDaemonIn)
22 {
23     clsTCPClt = nullptr;
24     clsUSBClt = nullptr;
25 #ifdef HDC_SUPPORT_UART
26     clsUARTClt = nullptr;
27 #endif
28     clsServerForClient = nullptr;
29     uv_rwlock_init(&daemonAdmin);
30     uv_rwlock_init(&forwardAdmin);
31 }
32 
~HdcServer()33 HdcServer::~HdcServer()
34 {
35     WRITE_LOG(LOG_DEBUG, "~HdcServer");
36     uv_rwlock_destroy(&daemonAdmin);
37     uv_rwlock_destroy(&forwardAdmin);
38 }
39 
ClearInstanceResource()40 void HdcServer::ClearInstanceResource()
41 {
42     TryStopInstance();
43     Base::TryCloseLoop(&loopMain, "HdcServer::~HdcServer");
44     if (clsTCPClt) {
45         delete clsTCPClt;
46     }
47     if (clsUSBClt) {
48         delete clsUSBClt;
49     }
50 #ifdef HDC_SUPPORT_UART
51     if (clsUARTClt) {
52         delete clsUARTClt;
53     }
54 #endif
55     if (clsServerForClient) {
56         delete (static_cast<HdcServerForClient *>(clsServerForClient));
57     }
58 }
59 
TryStopInstance()60 void HdcServer::TryStopInstance()
61 {
62     ClearSessions();
63     if (clsTCPClt) {
64         clsTCPClt->Stop();
65     }
66     if (clsUSBClt) {
67         clsUSBClt->Stop();
68     }
69 #ifdef HDC_SUPPORT_UART
70     if (clsUARTClt) {
71         clsUARTClt->Stop();
72     }
73 #endif
74     if (clsServerForClient) {
75         ((HdcServerForClient *)clsServerForClient)->Stop();
76     }
77     ReMainLoopForInstanceClear();
78     ClearMapDaemonInfo();
79 }
80 
Initial(const char * listenString)81 bool HdcServer::Initial(const char *listenString)
82 {
83     if (Base::ProgramMutex(SERVER_NAME.c_str(), false) != 0) {
84         WRITE_LOG(LOG_FATAL, "Other instance already running, program mutex failed");
85         return false;
86     }
87     Base::RemoveLogFile();
88     clsServerForClient = new HdcServerForClient(true, listenString, this, &loopMain);
89     int rc = (static_cast<HdcServerForClient *>(clsServerForClient))->Initial();
90     if (rc != RET_SUCCESS) {
91         WRITE_LOG(LOG_FATAL, "clsServerForClient Initial failed");
92         return false;
93     }
94     clsUSBClt->InitLogging(ctxUSB);
95     clsTCPClt = new HdcHostTCP(true, this);
96     clsUSBClt = new HdcHostUSB(true, this, ctxUSB);
97     if (clsUSBClt->Initial() != RET_SUCCESS) {
98         WRITE_LOG(LOG_FATAL, "clsUSBClt Initial failed");
99         return false;
100     }
101     if (!clsServerForClient || !clsTCPClt || !clsUSBClt) {
102         WRITE_LOG(LOG_FATAL, "Class init failed");
103         return false;
104     }
105 
106 #ifdef HDC_SUPPORT_UART
107     clsUARTClt = new HdcHostUART(*this);
108     if (!clsUARTClt) {
109         WRITE_LOG(LOG_FATAL, "Class init failed");
110         return false;
111     }
112     if (clsUARTClt->Initial() != RET_SUCCESS) {
113         WRITE_LOG(LOG_FATAL, "clsUARTClt Class init failed.");
114         return false;
115     }
116 #endif
117     return true;
118 }
119 
PullupServerWin32(const char * path,const char * listenString)120 bool HdcServer::PullupServerWin32(const char *path, const char *listenString)
121 {
122     bool retVal = false;
123 #ifdef _WIN32
124     char buf[BUF_SIZE_SMALL] = "";
125     char shortPath[MAX_PATH] = "";
126     std::string strPath = Base::UnicodeToUtf8(path, true);
127     int ret = GetShortPathName(strPath.c_str(), shortPath, MAX_PATH);
128     std::string runPath = shortPath;
129     if (ret == 0) {
130         int err = GetLastError();
131         constexpr int bufSize = 1024;
132         char buffer[bufSize] = { 0 };
133         strerror_s(buffer, bufSize, err);
134         WRITE_LOG(LOG_WARN, "GetShortPath path:[%s] errmsg:%s", path, buffer);
135         string uvPath = path;
136         runPath = uvPath.substr(uvPath.find_last_of("/\\") + 1);
137     }
138     WRITE_LOG(LOG_DEBUG, "server shortpath:[%s] runPath:[%s]", shortPath, runPath.c_str());
139     // here we give a dummy option first, because getopt will assume the first option is command. it
140     // begin from 2nd args.
141     if (sprintf_s(buf, sizeof(buf), "dummy -l %d -s %s -m", Base::GetLogLevelByEnv(), listenString) < 0) {
142         return retVal;
143     }
144     WRITE_LOG(LOG_DEBUG, "Run server in debug-forground, cmd:%s, args:%s", runPath.c_str(), buf);
145     STARTUPINFO si = {};
146     si.cb = sizeof(STARTUPINFO);
147     PROCESS_INFORMATION pi = {};
148 #ifndef HDC_DEBUG
149     si.dwFlags = STARTF_USESHOWWINDOW;
150     si.wShowWindow = SW_HIDE;
151 #endif
152     if (!CreateProcess(runPath.c_str(), buf, nullptr, nullptr, false, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi)) {
153         WRITE_LOG(LOG_WARN, "CreateProcess failed with cmd:%s, args:%s, Error Code %d", runPath.c_str(), buf,
154                   GetLastError());
155         retVal = false;
156     } else {
157         retVal = true;
158     }
159     CloseHandle(pi.hThread);
160     CloseHandle(pi.hProcess);
161 #endif
162     return retVal;
163 }
164 
165 // Only detects that the default call is in the loop address, the other tubes are not
PullupServer(const char * listenString)166 bool HdcServer::PullupServer(const char *listenString)
167 {
168     char path[BUF_SIZE_SMALL] = "";
169     size_t nPathSize = sizeof(path);
170     int ret = uv_exepath(path, &nPathSize);
171     if (ret < 0) {
172         constexpr int bufSize = 1024;
173         char buf[bufSize] = { 0 };
174         uv_err_name_r(ret, buf, bufSize);
175         WRITE_LOG(LOG_WARN, "uvexepath ret:%d error:%s", ret, buf);
176         return false;
177     }
178 
179 #ifdef _WIN32
180     if (!PullupServerWin32(path, listenString)) {
181         return false;
182     }
183 #else
184     pid_t pc = fork();  // create process as daemon process
185     if (pc < 0) {
186         return false;
187     } else if (!pc) {
188         int i;
189         const int maxFD = 1024;
190         for (i = 0; i < maxFD; ++i) {
191             // close file pipe
192             int fd = i;
193             Base::CloseFd(fd);
194         }
195         execl(path, "hdc", "-m", "-s", listenString, nullptr);
196         exit(0);
197         return true;
198     }
199     // orig process
200 #endif
201     // wait little time, util backend-server work ready
202     uv_sleep(TIME_BASE);
203     return true;
204 }
205 
ClearMapDaemonInfo()206 void HdcServer::ClearMapDaemonInfo()
207 {
208     map<string, HDaemonInfo>::iterator iter;
209     uv_rwlock_rdlock(&daemonAdmin);
210     for (iter = mapDaemon.begin(); iter != mapDaemon.end();) {
211         string sKey = iter->first;
212         HDaemonInfo hDi = iter->second;
213         delete hDi;
214         ++iter;
215     }
216     uv_rwlock_rdunlock(&daemonAdmin);
217     uv_rwlock_wrlock(&daemonAdmin);
218     mapDaemon.clear();
219     uv_rwlock_wrunlock(&daemonAdmin);
220 }
221 
BuildDaemonVisableLine(HDaemonInfo hdi,bool fullDisplay,string & out)222 void HdcServer::BuildDaemonVisableLine(HDaemonInfo hdi, bool fullDisplay, string &out)
223 {
224     if (fullDisplay) {
225         string sConn = conTypeDetail[CONN_UNKNOWN];
226         if (hdi->connType < CONN_UNKNOWN) {
227             sConn = conTypeDetail[hdi->connType];
228         }
229 
230         string sStatus = conStatusDetail[STATUS_UNKNOW];
231         if (hdi->connStatus < STATUS_UNAUTH) {
232             if (hdi->connStatus == STATUS_CONNECTED && hdi->daemonAuthStatus == DAEOMN_UNAUTHORIZED) {
233                 sStatus = conStatusDetail[STATUS_UNAUTH];
234             } else {
235                 sStatus = conStatusDetail[hdi->connStatus];
236             }
237         }
238 
239         string devname = hdi->devName;
240         if (devname.empty()) {
241             devname = "unknown...";
242         }
243         out = Base::StringFormat("%s\t\t%s\t%s\t%s\n", hdi->connectKey.c_str(), sConn.c_str(), sStatus.c_str(),
244                                  devname.c_str());
245     } else {
246         if (hdi->connStatus == STATUS_CONNECTED) {
247             out = Base::StringFormat("%s", hdi->connectKey.c_str());
248             if (hdi->daemonAuthStatus == DAEOMN_UNAUTHORIZED) {
249                 out.append("\tUnauthorized");
250             }
251             out.append("\n");
252         }
253     }
254 }
255 
GetDaemonMapList(uint8_t opType)256 string HdcServer::GetDaemonMapList(uint8_t opType)
257 {
258     string ret;
259     bool fullDisplay = false;
260     if (opType == OP_GET_STRLIST_FULL) {
261         fullDisplay = true;
262     }
263     uv_rwlock_rdlock(&daemonAdmin);
264     map<string, HDaemonInfo>::iterator iter;
265     string echoLine;
266     for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) {
267         HDaemonInfo di = iter->second;
268         if (!di) {
269             continue;
270         }
271         echoLine = "";
272         BuildDaemonVisableLine(di, fullDisplay, echoLine);
273         ret += echoLine;
274     }
275     uv_rwlock_rdunlock(&daemonAdmin);
276     return ret;
277 }
278 
GetDaemonMapOnlyOne(HDaemonInfo & hDaemonInfoInOut)279 void HdcServer::GetDaemonMapOnlyOne(HDaemonInfo &hDaemonInfoInOut)
280 {
281     uv_rwlock_rdlock(&daemonAdmin);
282     string key;
283     for (auto &i : mapDaemon) {
284         if (i.second->connStatus == STATUS_CONNECTED) {
285             if (key == STRING_EMPTY) {
286                 key = i.first;
287             } else {
288                 key = STRING_EMPTY;
289                 break;
290             }
291         }
292     }
293     if (key.size() > 0) {
294         hDaemonInfoInOut = mapDaemon[key];
295     }
296     uv_rwlock_rdunlock(&daemonAdmin);
297 }
298 
AdminDaemonMapForWait(const string & connectKey,HDaemonInfo & hDaemonInfoInOut)299 void HdcServer::AdminDaemonMapForWait(const string &connectKey, HDaemonInfo &hDaemonInfoInOut)
300 {
301     map<string, HDaemonInfo>::iterator iter;
302     for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) {
303         HDaemonInfo di = iter->second;
304         if (di->connStatus == STATUS_CONNECTED) {
305             if (!connectKey.empty() && connectKey != di->connectKey) {
306                 continue;
307             }
308             hDaemonInfoInOut = di;
309             return;
310         }
311     }
312     return;
313 }
314 
AdminDaemonMap(uint8_t opType,const string & connectKey,HDaemonInfo & hDaemonInfoInOut)315 string HdcServer::AdminDaemonMap(uint8_t opType, const string &connectKey, HDaemonInfo &hDaemonInfoInOut)
316 {
317     string sRet;
318     switch (opType) {
319         case OP_ADD: {
320             HDaemonInfo pdiNew = new(std::nothrow) HdcDaemonInformation();
321             if (pdiNew == nullptr) {
322                 WRITE_LOG(LOG_FATAL, "AdminDaemonMap new pdiNew failed");
323                 break;
324             }
325             *pdiNew = *hDaemonInfoInOut;
326             uv_rwlock_wrlock(&daemonAdmin);
327             if (!mapDaemon[hDaemonInfoInOut->connectKey]) {
328                 mapDaemon[hDaemonInfoInOut->connectKey] = pdiNew;
329             }
330             uv_rwlock_wrunlock(&daemonAdmin);
331             break;
332         }
333         case OP_GET_STRLIST:
334         case OP_GET_STRLIST_FULL: {
335             sRet = GetDaemonMapList(opType);
336             break;
337         }
338         case OP_QUERY: {
339             uv_rwlock_rdlock(&daemonAdmin);
340             if (mapDaemon.count(connectKey)) {
341                 hDaemonInfoInOut = mapDaemon[connectKey];
342             }
343             uv_rwlock_rdunlock(&daemonAdmin);
344             break;
345         }
346         case OP_REMOVE: {
347             uv_rwlock_wrlock(&daemonAdmin);
348             if (mapDaemon.count(connectKey)) {
349                 mapDaemon.erase(connectKey);
350             }
351             uv_rwlock_wrunlock(&daemonAdmin);
352             break;
353         }
354         case OP_GET_ANY: {
355             uv_rwlock_rdlock(&daemonAdmin);
356             map<string, HDaemonInfo>::iterator iter;
357             for (iter = mapDaemon.begin(); iter != mapDaemon.end(); ++iter) {
358                 HDaemonInfo di = iter->second;
359                 // usb will be auto connected
360                 if (di->connStatus == STATUS_READY || di->connStatus == STATUS_CONNECTED) {
361                     hDaemonInfoInOut = di;
362                     break;
363                 }
364             }
365             uv_rwlock_rdunlock(&daemonAdmin);
366             break;
367         }
368         case OP_WAIT_FOR_ANY: {
369             uv_rwlock_rdlock(&daemonAdmin);
370             AdminDaemonMapForWait(connectKey, hDaemonInfoInOut);
371             uv_rwlock_rdunlock(&daemonAdmin);
372             break;
373         }
374         case OP_GET_ONLY: {
375             GetDaemonMapOnlyOne(hDaemonInfoInOut);
376             break;
377         }
378         case OP_UPDATE: {  // Cannot update the Object HDi lower key value by direct value
379             uv_rwlock_wrlock(&daemonAdmin);
380             HDaemonInfo hdi = mapDaemon[hDaemonInfoInOut->connectKey];
381             if (hdi) {
382                 *mapDaemon[hDaemonInfoInOut->connectKey] = *hDaemonInfoInOut;
383             }
384             uv_rwlock_wrunlock(&daemonAdmin);
385             break;
386         }
387         default:
388             break;
389     }
390     return sRet;
391 }
392 
NotifyInstanceSessionFree(HSession hSession,bool freeOrClear)393 void HdcServer::NotifyInstanceSessionFree(HSession hSession, bool freeOrClear)
394 {
395     HDaemonInfo hdiOld = nullptr;
396     AdminDaemonMap(OP_QUERY, hSession->connectKey, hdiOld);
397     if (hdiOld == nullptr) {
398         WRITE_LOG(LOG_FATAL, "NotifyInstanceSessionFree hdiOld nullptr");
399         return;
400     }
401     if (!freeOrClear) {  // step1
402         // update
403         HdcDaemonInformation diNew = *hdiOld;
404         diNew.connStatus = STATUS_OFFLINE;
405         HDaemonInfo hdiNew = &diNew;
406         AdminDaemonMap(OP_UPDATE, hSession->connectKey, hdiNew);
407         CleanForwardMap(hSession->sessionId);
408     } else {  // step2
409         string usbMountPoint = hdiOld->usbMountPoint;
410         // The waiting time must be longer than DEVICE_CHECK_INTERVAL. Wait the method WatchUsbNodeChange
411         // to finish execution. Otherwise, the main thread and the session worker thread will conflict
412         constexpr int waitDaemonReconnect = DEVICE_CHECK_INTERVAL + DEVICE_CHECK_INTERVAL;
413         auto funcDelayUsbNotify = [this, usbMountPoint](const uint8_t flag, string &msg, const void *) -> void {
414             string s = usbMountPoint;
415             clsUSBClt->RemoveIgnoreDevice(s);
416         };
417         if (usbMountPoint.size() > 0) {
418             // wait time for daemon reconnect
419             // If removed from maplist, the USB module will be reconnected, so it needs to wait for a while
420             Base::DelayDoSimple(&loopMain, waitDaemonReconnect, funcDelayUsbNotify);
421         }
422     }
423 }
424 
GetDaemonAuthType(HSession hSession,SessionHandShake & handshake)425 void HdcServer::GetDaemonAuthType(HSession hSession, SessionHandShake &handshake)
426 {
427     /*
428      * check if daemon support RSA_3072_SHA512 for auth
429      * it the value is not RSA_3072_SHA512, we use old auth algorithm
430      * Notice, If deamon is old version 'handshake.buf' will be 'hSession->tokenRSA',
431      * the length of hSession->tokenRSA less than min len(TLV_MIN_LEN), so there no
432      * problem
433     */
434     std::map<string, string> tlvmap;
435     hSession->verifyType = AuthVerifyType::RSA_ENCRYPT;
436     if (!Base::TlvToStringMap(handshake.buf, tlvmap)) {
437         WRITE_LOG(LOG_INFO, "the deamon maybe old version for %u session, so use rsa encrypt", hSession->sessionId);
438         return;
439     }
440     if (tlvmap.find(TAG_AUTH_TYPE) == tlvmap.end() ||
441         tlvmap[TAG_AUTH_TYPE] != std::to_string(AuthVerifyType::RSA_3072_SHA512)) {
442         WRITE_LOG(LOG_FATAL, "the buf is invalid for %u session, so use rsa encrypt", hSession->sessionId);
443         return;
444     }
445     hSession->verifyType = AuthVerifyType::RSA_3072_SHA512;
446     WRITE_LOG(LOG_INFO, "daemon auth type is rsa_3072_sha512 for %u session", hSession->sessionId);
447 }
448 
HandServerAuth(HSession hSession,SessionHandShake & handshake)449 bool HdcServer::HandServerAuth(HSession hSession, SessionHandShake &handshake)
450 {
451     string bufString;
452     switch (handshake.authType) {
453         case AUTH_PUBLICKEY: {
454             WRITE_LOG(LOG_INFO, "recive get publickey cmd");
455             GetDaemonAuthType(hSession, handshake);
456             if (!HdcAuth::GetPublicKeyinfo(handshake.buf)) {
457                 WRITE_LOG(LOG_FATAL, "load public key failed");
458                 return false;
459             }
460             handshake.authType = AUTH_PUBLICKEY;
461             bufString = SerialStruct::SerializeToString(handshake);
462             Send(hSession->sessionId, 0, CMD_KERNEL_HANDSHAKE,
463                  reinterpret_cast<uint8_t *>(const_cast<char *>(bufString.c_str())), bufString.size());
464 
465             WRITE_LOG(LOG_INFO, "send pubkey over");
466             return true;
467         }
468         case AUTH_SIGNATURE: {
469             WRITE_LOG(LOG_INFO, "recive auth signture cmd");
470             if (!HdcAuth::RsaSignAndBase64(handshake.buf, hSession->verifyType)) {
471                 WRITE_LOG(LOG_FATAL, "sign failed");
472                 return false;
473             }
474             handshake.authType = AUTH_SIGNATURE;
475             bufString = SerialStruct::SerializeToString(handshake);
476             Send(hSession->sessionId, 0, CMD_KERNEL_HANDSHAKE,
477                  reinterpret_cast<uint8_t *>(const_cast<char *>(bufString.c_str())), bufString.size());
478             WRITE_LOG(LOG_INFO, "response auth signture success");
479             return true;
480         }
481         default:
482             WRITE_LOG(LOG_FATAL, "invalid auth type %d", handshake.authType);
483             return false;
484     }
485 }
486 
UpdateHdiInfo(Hdc::HdcSessionBase::SessionHandShake & handshake,const string & connectKey)487 void HdcServer::UpdateHdiInfo(Hdc::HdcSessionBase::SessionHandShake &handshake, const string &connectKey)
488 {
489     HDaemonInfo hdiOld = nullptr;
490     AdminDaemonMap(OP_QUERY, connectKey, hdiOld);
491     if (!hdiOld) {
492         return;
493     }
494     HdcDaemonInformation diNew = *hdiOld;
495     HDaemonInfo hdiNew = &diNew;
496     // update
497     hdiNew->connStatus = STATUS_CONNECTED;
498     WRITE_LOG(LOG_INFO, "handshake info is : %s", handshake.ToDebugString().c_str());
499     WRITE_LOG(LOG_INFO, "handshake.buf = %s", handshake.buf.c_str());
500     if (handshake.version < "Ver: 3.0.0b") {
501         if (!handshake.buf.empty()) {
502             hdiNew->devName = handshake.buf;
503         }
504     } else {
505         std::map<string, string> tlvmap;
506         if (Base::TlvToStringMap(handshake.buf, tlvmap)) {
507             if (tlvmap.find(TAG_DEVNAME) != tlvmap.end()) {
508                 hdiNew->devName = tlvmap[TAG_DEVNAME];
509                 WRITE_LOG(LOG_INFO, "devname = %s", hdiNew->devName.c_str());
510             }
511             if (tlvmap.find(TAG_EMGMSG) != tlvmap.end()) {
512                 hdiNew->emgmsg = tlvmap[TAG_EMGMSG];
513                 WRITE_LOG(LOG_INFO, "emgmsg = %s", hdiNew->emgmsg.c_str());
514             }
515             if (tlvmap.find(TAG_DAEOMN_AUTHSTATUS) != tlvmap.end()) {
516                 hdiNew->daemonAuthStatus = tlvmap[TAG_DAEOMN_AUTHSTATUS];
517                 WRITE_LOG(LOG_INFO, "daemonauthstatus = %s", hdiNew->daemonAuthStatus.c_str());
518             }
519             if (tlvmap.find(TAG_FEATURE_SHELL_OPT) != tlvmap.end()) {
520                 hdiNew->daemonFeature[TAG_FEATURE_SHELL_OPT] = tlvmap[TAG_FEATURE_SHELL_OPT];
521                 WRITE_LOG(LOG_INFO, "shellOpt = %s", hdiNew->daemonFeature[TAG_FEATURE_SHELL_OPT].c_str());
522             }
523         } else {
524             WRITE_LOG(LOG_FATAL, "TlvToStringMap failed");
525         }
526     }
527     hdiNew->version = handshake.version;
528     AdminDaemonMap(OP_UPDATE, connectKey, hdiNew);
529 }
530 
ServerSessionHandshake(HSession hSession,uint8_t * payload,int payloadSize)531 bool HdcServer::ServerSessionHandshake(HSession hSession, uint8_t *payload, int payloadSize)
532 {
533     // session handshake step3
534     string s = string(reinterpret_cast<char *>(payload), payloadSize);
535     Hdc::HdcSessionBase::SessionHandShake handshake;
536     SerialStruct::ParseFromString(handshake, s);
537 #ifdef HDC_DEBUG
538     WRITE_LOG(LOG_DEBUG, "handshake.banner:%s, payload:%s(%d)", handshake.banner.c_str(), s.c_str(), payloadSize);
539 #endif
540 
541     if (handshake.banner == HANDSHAKE_FAILED.c_str()) {
542         WRITE_LOG(LOG_FATAL, "Handshake failed");
543         return false;
544     }
545 
546     if (handshake.banner != HANDSHAKE_MESSAGE.c_str()) {
547         WRITE_LOG(LOG_DEBUG, "Hello failed");
548         return false;
549     }
550     if (handshake.authType != AUTH_OK) {
551         if (!HandServerAuth(hSession, handshake)) {
552             WRITE_LOG(LOG_WARN, "Auth failed");
553             return false;
554         }
555         return true;
556     }
557     // handshake auth OK
558     UpdateHdiInfo(handshake, hSession->connectKey);
559     hSession->handshakeOK = true;
560     return true;
561 }
562 
563 // call in child thread
FetchCommand(HSession hSession,const uint32_t channelId,const uint16_t command,uint8_t * payload,const int payloadSize)564 bool HdcServer::FetchCommand(HSession hSession, const uint32_t channelId, const uint16_t command, uint8_t *payload,
565                              const int payloadSize)
566 {
567     bool ret = true;
568     HdcServerForClient *sfc = static_cast<HdcServerForClient *>(clsServerForClient);
569     if (command == CMD_KERNEL_HANDSHAKE) {
570         ret = ServerSessionHandshake(hSession, payload, payloadSize);
571         WRITE_LOG(LOG_DEBUG, "Session handshake %s connType:%d", ret ? "successful" : "failed",
572                   hSession->connType);
573         return ret;
574     }
575     // When you first initialize, ChannelID may be 0
576     HChannel hChannel = sfc->AdminChannel(OP_QUERY_REF, channelId, nullptr);
577     if (!hChannel) {
578         if (command == CMD_KERNEL_CHANNEL_CLOSE) {
579             // Daemon close channel and want to notify server close channel also, but it may has been
580             // closed by herself
581             WRITE_LOG(LOG_WARN, "Die channelId :%lu recv CMD_KERNEL_CHANNEL_CLOSE", channelId);
582         } else {
583             // Client may be ctrl+c and Server remove channel. notify server async
584             WRITE_LOG(LOG_DEBUG, "channelId :%lu die", channelId);
585         }
586         uint8_t flag = 0;
587         Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, &flag, 1);
588         return ret;
589     }
590     if (hChannel->isDead) {
591         WRITE_LOG(LOG_FATAL, "FetchCommand channelId:%u isDead", channelId);
592         --hChannel->ref;
593         return ret;
594     }
595     switch (command) {
596         case CMD_KERNEL_ECHO_RAW: {  // Native shell data output
597             sfc->EchoClientRaw(hChannel, payload, payloadSize);
598             break;
599         }
600         case CMD_KERNEL_ECHO: {
601             MessageLevel level = static_cast<MessageLevel>(*payload);
602             string s(reinterpret_cast<char *>(payload + 1), payloadSize - 1);
603             sfc->EchoClient(hChannel, level, s.c_str());
604             WRITE_LOG(LOG_INFO, "CMD_KERNEL_ECHO size:%d channelId:%u", payloadSize - 1, channelId);
605             break;
606         }
607         case CMD_KERNEL_CHANNEL_CLOSE: {
608             WRITE_LOG(LOG_DEBUG, "CMD_KERNEL_CHANNEL_CLOSE channelid:%u", channelId);
609             // Forcibly closing the tcp handle here may result in incomplete data reception on the client side
610             ClearOwnTasks(hSession, channelId);
611             // crossthread free
612             sfc->PushAsyncMessage(channelId, ASYNC_FREE_CHANNEL, nullptr, 0);
613             if (*payload != 0) {
614                 --(*payload);
615                 Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, payload, 1);
616             }
617             break;
618         }
619         case CMD_FORWARD_SUCCESS: {
620             // add to local
621             HdcForwardInformation di;
622             HForwardInfo pdiNew = &di;
623             pdiNew->channelId = channelId;
624             pdiNew->sessionId = hSession->sessionId;
625             pdiNew->connectKey = hSession->connectKey;
626             pdiNew->forwardDirection = (reinterpret_cast<char *>(payload))[0] == '1';
627             pdiNew->taskString = reinterpret_cast<char *>(payload);
628             AdminForwardMap(OP_ADD, STRING_EMPTY, pdiNew);
629             Base::TryCloseHandle((uv_handle_t *)&hChannel->hChildWorkTCP);  // detch client channel
630             break;
631         }
632         case CMD_FILE_INIT:
633         case CMD_FILE_CHECK:
634         case CMD_FILE_BEGIN:
635         case CMD_FILE_DATA:
636         case CMD_FILE_FINISH:
637         case CMD_FILE_MODE:
638         case CMD_DIR_MODE:
639         case CMD_APP_INIT:
640         case CMD_APP_CHECK:
641         case CMD_APP_BEGIN:
642         case CMD_APP_DATA:
643         case CMD_APP_FINISH:
644             if (hChannel->fromClient) {
645                 // server directly passthrough app command to client if remote file mode, else go default
646                 sfc->SendCommandToClient(hChannel, command, payload, payloadSize);
647                 break;
648             }
649         default: {
650             HSession hSessionByQuery = AdminSession(OP_QUERY, hChannel->targetSessionId, nullptr);
651             if (!hSessionByQuery) {
652                 ret = false;
653                 break;
654             }
655             ret = DispatchTaskData(hSessionByQuery, channelId, command, payload, payloadSize);
656             break;
657         }
658     }
659     --hChannel->ref;
660     return ret;
661 }
662 
BuildForwardVisableLine(bool fullOrSimble,HForwardInfo hfi,string & echo)663 void HdcServer::BuildForwardVisableLine(bool fullOrSimble, HForwardInfo hfi, string &echo)
664 {
665     string buf;
666     if (fullOrSimble) {
667         buf = Base::StringFormat("%s    %s    %s\n", hfi->connectKey.c_str(), hfi->taskString.substr(OFFSET).c_str(),
668                                  hfi->forwardDirection ? "[Forward]" : "[Reverse]");
669     } else {
670         buf = Base::StringFormat("%s\n", hfi->taskString.c_str());
671     }
672     echo += buf;
673 }
674 
AdminForwardMap(uint8_t opType,const string & taskString,HForwardInfo & hForwardInfoInOut)675 string HdcServer::AdminForwardMap(uint8_t opType, const string &taskString, HForwardInfo &hForwardInfoInOut)
676 {
677     string sRet;
678     switch (opType) {
679         case OP_ADD: {
680             HForwardInfo pfiNew = new(std::nothrow) HdcForwardInformation();
681             if (pfiNew == nullptr) {
682                 WRITE_LOG(LOG_FATAL, "AdminForwardMap new pfiNew failed");
683                 break;
684             }
685             *pfiNew = *hForwardInfoInOut;
686             uv_rwlock_wrlock(&forwardAdmin);
687             if (!mapForward[hForwardInfoInOut->taskString]) {
688                 mapForward[hForwardInfoInOut->taskString] = pfiNew;
689             }
690             uv_rwlock_wrunlock(&forwardAdmin);
691             break;
692         }
693         case OP_GET_STRLIST:
694         case OP_GET_STRLIST_FULL: {
695             uv_rwlock_rdlock(&forwardAdmin);
696             map<string, HForwardInfo>::iterator iter;
697             for (iter = mapForward.begin(); iter != mapForward.end(); ++iter) {
698                 HForwardInfo di = iter->second;
699                 if (!di) {
700                     continue;
701                 }
702                 BuildForwardVisableLine(opType == OP_GET_STRLIST_FULL, di, sRet);
703             }
704             uv_rwlock_rdunlock(&forwardAdmin);
705             break;
706         }
707         case OP_QUERY: {
708             uv_rwlock_rdlock(&forwardAdmin);
709             if (mapForward.count(taskString)) {
710                 hForwardInfoInOut = mapForward[taskString];
711             }
712             uv_rwlock_rdunlock(&forwardAdmin);
713             break;
714         }
715         case OP_REMOVE: {
716             uv_rwlock_wrlock(&forwardAdmin);
717             if (mapForward.count(taskString)) {
718                 mapForward.erase(taskString);
719             }
720             uv_rwlock_wrunlock(&forwardAdmin);
721             break;
722         }
723         default:
724             break;
725     }
726     return sRet;
727 }
728 
CleanForwardMap(uint32_t sessionId)729 void HdcServer::CleanForwardMap(uint32_t sessionId)
730 {
731     uv_rwlock_rdlock(&forwardAdmin);
732     map<string, HForwardInfo>::iterator iter;
733     for (iter = mapForward.begin(); iter != mapForward.end();) {
734         HForwardInfo di = iter->second;
735         if (!di) {
736             continue;
737         }
738         if (sessionId == 0 || sessionId == di->sessionId) {
739             iter = mapForward.erase(iter);
740         } else {
741             iter++;
742         }
743     }
744     uv_rwlock_rdunlock(&forwardAdmin);
745 }
746 
UsbPreConnect(uv_timer_t * handle)747 void HdcServer::UsbPreConnect(uv_timer_t *handle)
748 {
749     HSession hSession = (HSession)handle->data;
750     bool stopLoop = false;
751     HdcServer *hdcServer = (HdcServer *)hSession->classInstance;
752     while (true) {
753         WRITE_LOG(LOG_DEBUG, "HdcServer::UsbPreConnect");
754         HDaemonInfo pDi = nullptr;
755         if (hSession->connectKey == "any") {
756             hdcServer->AdminDaemonMap(OP_GET_ANY, hSession->connectKey, pDi);
757         } else {
758             hdcServer->AdminDaemonMap(OP_QUERY, hSession->connectKey, pDi);
759         }
760         if (!pDi || !pDi->usbMountPoint.size()) {
761             break;
762         }
763         HdcHostUSB *hdcHostUSB = (HdcHostUSB *)hSession->classModule;
764         hdcHostUSB->ConnectDetectDaemon(hSession, pDi);
765         stopLoop = true;
766         break;
767     }
768     if (stopLoop && !uv_is_closing((const uv_handle_t *)handle)) {
769         uv_close((uv_handle_t *)handle, Base::CloseTimerCallback);
770     }
771 }
772 #ifdef HDC_SUPPORT_UART
UartPreConnect(uv_timer_t * handle)773 void HdcServer::UartPreConnect(uv_timer_t *handle)
774 {
775     WRITE_LOG(LOG_DEBUG, "%s", __FUNCTION__);
776     HSession hSession = (HSession)handle->data;
777     bool stopLoop = false;
778     HdcServer *hdcServer = (HdcServer *)hSession->classInstance;
779     const int uartConnectRetryMax = 100; // max 6s
780     while (true) {
781         if (hSession->hUART->retryCount > uartConnectRetryMax) {
782             WRITE_LOG(LOG_DEBUG, "%s failed because max retry limit %d", __FUNCTION__,
783                       hSession->hUART->retryCount);
784             hdcServer->FreeSession(hSession->sessionId);
785             stopLoop = true;
786             break;
787         }
788         hSession->hUART->retryCount++;
789         HDaemonInfo pDi = nullptr;
790 
791         WRITE_LOG(LOG_DEBUG, "%s query %s", __FUNCTION__, hSession->ToDebugString().c_str());
792         hdcServer->AdminDaemonMap(OP_QUERY, hSession->connectKey, pDi);
793         if (!pDi) {
794             WRITE_LOG(LOG_DEBUG, "%s not found", __FUNCTION__);
795             break;
796         }
797         HdcHostUART *hdcHostUART = (HdcHostUART *)hSession->classModule;
798         hdcHostUART->ConnectDaemonByUart(hSession, pDi);
799         WRITE_LOG(LOG_DEBUG, "%s ConnectDaemonByUart done", __FUNCTION__);
800 
801         stopLoop = true;
802         break;
803     }
804     if (stopLoop) {
805         uv_close((uv_handle_t *)handle, Base::CloseTimerCallback);
806     }
807 }
808 
CreatConnectUart(HSession hSession)809 void HdcServer::CreatConnectUart(HSession hSession)
810 {
811     uv_timer_t *waitTimeDoCmd = new(std::nothrow) uv_timer_t;
812     if (waitTimeDoCmd == nullptr) {
813         WRITE_LOG(LOG_FATAL, "CreatConnectUart new waitTimeDoCmd failed");
814         return;
815     }
816     uv_timer_init(&loopMain, waitTimeDoCmd);
817     waitTimeDoCmd->data = hSession;
818     uv_timer_start(waitTimeDoCmd, UartPreConnect, UV_TIMEOUT, UV_REPEAT);
819 }
820 #endif
821 // -1,has old,-2 error
CreateConnect(const string & connectKey,bool isCheck)822 int HdcServer::CreateConnect(const string &connectKey, bool isCheck)
823 {
824     uint8_t connType = 0;
825     if (connectKey.find(":") != std::string::npos) { // TCP
826         connType = CONN_TCP;
827     }
828 #ifdef HDC_SUPPORT_UART
829     else if (connectKey.find("COM") == 0 ||
830              connectKey.find("/dev/ttyUSB") == 0 ||
831              connectKey.find("/dev/cu.") == 0) { // UART
832         connType = CONN_SERIAL;
833     }
834 #endif
835     else { // USB
836         connType = CONN_USB;
837     }
838     HDaemonInfo hdi = nullptr;
839     if (connectKey == "any") {
840         return RET_SUCCESS;
841     }
842     AdminDaemonMap(OP_QUERY, connectKey, hdi);
843     if (hdi == nullptr) {
844         HdcDaemonInformation di = {};
845         di.connectKey = connectKey;
846         di.connType = connType;
847         di.connStatus = STATUS_UNKNOW;
848         HDaemonInfo pDi = reinterpret_cast<HDaemonInfo>(&di);
849         AdminDaemonMap(OP_ADD, "", pDi);
850         AdminDaemonMap(OP_QUERY, connectKey, hdi);
851     }
852     if (!hdi || hdi->connStatus == STATUS_CONNECTED) {
853         WRITE_LOG(LOG_FATAL, "Connected return");
854         return ERR_GENERIC;
855     }
856     HSession hSession = nullptr;
857     if (connType == CONN_TCP) {
858         hSession = clsTCPClt->ConnectDaemon(connectKey, isCheck);
859     } else if (connType == CONN_SERIAL) {
860 #ifdef HDC_SUPPORT_UART
861         clsUARTClt->SetCheckFlag(isCheck);
862         hSession = clsUARTClt->ConnectDaemon(connectKey);
863 #endif
864     } else {
865         hSession = MallocSession(true, CONN_USB, clsUSBClt);
866         if (!hSession) {
867             WRITE_LOG(LOG_FATAL, "CreateConnect malloc usb session failed %s", Hdc::MaskString(connectKey).c_str());
868             return ERR_BUF_ALLOC;
869         }
870         hSession->connectKey = connectKey;
871         uv_timer_t *waitTimeDoCmd = new(std::nothrow) uv_timer_t;
872         if (waitTimeDoCmd == nullptr) {
873             WRITE_LOG(LOG_FATAL, "CreateConnect new waitTimeDoCmd failed");
874             FreeSession(hSession->sessionId);
875             return ERR_GENERIC;
876         }
877         uv_timer_init(&loopMain, waitTimeDoCmd);
878         waitTimeDoCmd->data = hSession;
879         uv_timer_start(waitTimeDoCmd, UsbPreConnect, UV_TIMEOUT, UV_REPEAT);
880     }
881     if (!hSession) {
882         WRITE_LOG(LOG_FATAL, "CreateConnect hSession nullptr");
883         return ERR_BUF_ALLOC;
884     }
885     HDaemonInfo hdiQuery = nullptr;
886     AdminDaemonMap(OP_QUERY, connectKey, hdiQuery);
887     if (hdiQuery) {
888         HdcDaemonInformation diNew = *hdiQuery;
889         diNew.hSession = hSession;
890         HDaemonInfo hdiNew = &diNew;
891         AdminDaemonMap(OP_UPDATE, hdiQuery->connectKey, hdiNew);
892     }
893     return RET_SUCCESS;
894 }
895 
AttachChannel(HSession hSession,const uint32_t channelId)896 void HdcServer::AttachChannel(HSession hSession, const uint32_t channelId)
897 {
898     int ret = 0;
899     HdcServerForClient *hSfc = static_cast<HdcServerForClient *>(clsServerForClient);
900     HChannel hChannel = hSfc->AdminChannel(OP_QUERY_REF, channelId, nullptr);
901     if (!hChannel) {
902         WRITE_LOG(LOG_DEBUG, "AttachChannel hChannel null channelId:%u", channelId);
903         return;
904     }
905     uv_tcp_init(&hSession->childLoop, &hChannel->hChildWorkTCP);
906     hChannel->hChildWorkTCP.data = hChannel;
907     hChannel->targetSessionId = hSession->sessionId;
908     if ((ret = uv_tcp_open((uv_tcp_t *)&hChannel->hChildWorkTCP, hChannel->fdChildWorkTCP)) < 0) {
909         constexpr int bufSize = 1024;
910         char buf[bufSize] = { 0 };
911         uv_err_name_r(ret, buf, bufSize);
912         WRITE_LOG(LOG_WARN, "Hdcserver AttachChannel uv_tcp_open failed %s, channelid:%d fdChildWorkTCP:%d",
913                   buf, hChannel->channelId, hChannel->fdChildWorkTCP);
914         Base::TryCloseHandle((uv_handle_t *)&hChannel->hChildWorkTCP);
915         --hChannel->ref;
916         return;
917     }
918     Base::SetTcpOptions((uv_tcp_t *)&hChannel->hChildWorkTCP);
919     uv_read_start((uv_stream_t *)&hChannel->hChildWorkTCP, hSfc->AllocCallback, hSfc->ReadStream);
920     --hChannel->ref;
921 };
922 
DeatchChannel(HSession hSession,const uint32_t channelId)923 void HdcServer::DeatchChannel(HSession hSession, const uint32_t channelId)
924 {
925     HdcServerForClient *hSfc = static_cast<HdcServerForClient *>(clsServerForClient);
926     // childCleared has not set, no need OP_QUERY_REF
927     HChannel hChannel = hSfc->AdminChannel(OP_QUERY, channelId, nullptr);
928     if (!hChannel) {
929         ClearOwnTasks(hSession, channelId);
930         uint8_t count = 0;
931         Send(hSession->sessionId, channelId, CMD_KERNEL_CHANNEL_CLOSE, &count, 1);
932         WRITE_LOG(LOG_WARN, "DeatchChannel hChannel null channelId:%u", channelId);
933         return;
934     }
935     if (hChannel->childCleared) {
936         WRITE_LOG(LOG_DEBUG, "Childchannel has already freed, cid:%u", channelId);
937         return;
938     }
939     // The own task for this channel must be clear before free channel
940     ClearOwnTasks(hSession, channelId);
941     uint8_t count = 0;
942     Send(hSession->sessionId, hChannel->channelId, CMD_KERNEL_CHANNEL_CLOSE, &count, 1);
943     WRITE_LOG(LOG_DEBUG, "Childchannel begin close, cid:%u, sid:%u", hChannel->channelId, hSession->sessionId);
944     if (uv_is_closing((const uv_handle_t *)&hChannel->hChildWorkTCP)) {
945         Base::DoNextLoop(&hSession->childLoop, hChannel, [](const uint8_t flag, string &msg, const void *data) {
946             HChannel hChannel = (HChannel)data;
947             hChannel->childCleared = true;
948             WRITE_LOG(LOG_DEBUG, "Childchannel free direct, cid:%u", hChannel->channelId);
949         });
950     } else {
951         if (hChannel->hChildWorkTCP.loop == NULL) {
952             WRITE_LOG(LOG_DEBUG, "Childchannel loop is null, cid:%u", hChannel->channelId);
953         }
954         Base::TryCloseHandle((uv_handle_t *)&hChannel->hChildWorkTCP, [](uv_handle_t *handle) -> void {
955             HChannel hChannel = (HChannel)handle->data;
956             hChannel->childCleared = true;
957             WRITE_LOG(LOG_DEBUG, "Childchannel free callback, cid:%u", hChannel->channelId);
958         });
959     }
960 };
961 
ServerCommand(const uint32_t sessionId,const uint32_t channelId,const uint16_t command,uint8_t * bufPtr,const int size)962 bool HdcServer::ServerCommand(const uint32_t sessionId, const uint32_t channelId, const uint16_t command,
963                               uint8_t *bufPtr, const int size)
964 {
965     HdcServerForClient *hSfc = static_cast<HdcServerForClient *>(clsServerForClient);
966     HChannel hChannel = hSfc->AdminChannel(OP_QUERY, channelId, nullptr);
967     HSession hSession = AdminSession(OP_QUERY, sessionId, nullptr);
968     if (!hChannel || !hSession) {
969         return false;
970     }
971     return FetchCommand(hSession, channelId, command, bufPtr, size);
972 }
973 
974 // clang-format off
RedirectToTask(HTaskInfo hTaskInfo,HSession hSession,const uint32_t channelId,const uint16_t command,uint8_t * payload,const int payloadSize)975 bool HdcServer::RedirectToTask(HTaskInfo hTaskInfo, HSession hSession, const uint32_t channelId,
976                                const uint16_t command, uint8_t *payload, const int payloadSize)
977 // clang-format on
978 {
979     bool ret = true;
980     hTaskInfo->ownerSessionClass = this;
981     switch (command) {
982         case CMD_UNITY_BUGREPORT_INIT:
983         case CMD_UNITY_BUGREPORT_DATA:
984             ret = TaskCommandDispatch<HdcHostUnity>(hTaskInfo, TYPE_UNITY, command, payload, payloadSize);
985             break;
986         case CMD_FILE_INIT:
987         case CMD_FILE_BEGIN:
988         case CMD_FILE_CHECK:
989         case CMD_FILE_DATA:
990         case CMD_FILE_FINISH:
991         case CMD_FILE_MODE:
992         case CMD_DIR_MODE:
993             ret = TaskCommandDispatch<HdcFile>(hTaskInfo, TASK_FILE, command, payload, payloadSize);
994             break;
995         case CMD_FORWARD_INIT:
996         case CMD_FORWARD_CHECK:
997         case CMD_FORWARD_CHECK_RESULT:
998         case CMD_FORWARD_ACTIVE_MASTER:
999         case CMD_FORWARD_ACTIVE_SLAVE:
1000         case CMD_FORWARD_DATA:
1001         case CMD_FORWARD_FREE_CONTEXT:
1002             ret = TaskCommandDispatch<HdcHostForward>(hTaskInfo, TASK_FORWARD, command, payload, payloadSize);
1003             break;
1004         case CMD_APP_INIT:
1005         case CMD_APP_SIDELOAD:
1006         case CMD_APP_BEGIN:
1007         case CMD_APP_FINISH:
1008         case CMD_APP_UNINSTALL:
1009             ret = TaskCommandDispatch<HdcHostApp>(hTaskInfo, TASK_APP, command, payload, payloadSize);
1010             break;
1011         case CMD_FLASHD_UPDATE_INIT:
1012         case CMD_FLASHD_FLASH_INIT:
1013         case CMD_FLASHD_CHECK:
1014         case CMD_FLASHD_BEGIN:
1015         case CMD_FLASHD_DATA:
1016         case CMD_FLASHD_FINISH:
1017         case CMD_FLASHD_ERASE:
1018         case CMD_FLASHD_FORMAT:
1019         case CMD_FLASHD_PROGRESS:
1020             ret = TaskCommandDispatch<HostUpdater>(hTaskInfo, TASK_FLASHD, command, payload, payloadSize);
1021             break;
1022         default:
1023             // ignore unknown command
1024             break;
1025     }
1026     return ret;
1027 }
1028 
RemoveInstanceTask(const uint8_t op,HTaskInfo hTask)1029 bool HdcServer::RemoveInstanceTask(const uint8_t op, HTaskInfo hTask)
1030 {
1031     bool ret = true;
1032     switch (hTask->taskType) {
1033         case TYPE_SHELL:
1034             WRITE_LOG(LOG_DEBUG, "Server not enable unity/shell");
1035             break;
1036         case TYPE_UNITY:
1037             ret = DoTaskRemove<HdcHostUnity>(hTask, op);
1038             break;
1039         case TASK_FILE:
1040             ret = DoTaskRemove<HdcFile>(hTask, op);
1041             break;
1042         case TASK_FORWARD:
1043             ret = DoTaskRemove<HdcHostForward>(hTask, op);
1044             break;
1045         case TASK_APP:
1046             ret = DoTaskRemove<HdcHostApp>(hTask, op);
1047             break;
1048         case TASK_FLASHD:
1049             ret = DoTaskRemove<HostUpdater>(hTask, op);
1050             break;
1051         default:
1052             ret = false;
1053             break;
1054     }
1055     return ret;
1056 }
1057 
EchoToClientsForSession(uint32_t targetSessionId,const string & echo)1058 void HdcServer::EchoToClientsForSession(uint32_t targetSessionId, const string &echo)
1059 {
1060     HdcServerForClient *hSfc = static_cast<HdcServerForClient *>(clsServerForClient);
1061     WRITE_LOG(LOG_INFO, "%s:%u %s", __FUNCTION__, targetSessionId, echo.c_str());
1062     hSfc->EchoToAllChannelsViaSessionId(targetSessionId, echo);
1063 }
1064 }  // namespace Hdc
1065