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 */
16#include "HdcJdwpSimulator.h"
17using namespace OHOS;
18using namespace OHOS::HiviewDFX;
19static constexpr HiLogLabel LABEL = {LOG_CORE, 0, "JDWP_TEST"};
20HdcJdwpSimulator::HdcJdwpSimulator(uv_loop_t *loopIn, string pkg)
21{
22    loop = loopIn;
23    exit = false;
24    pkgName = pkg;
25}
26
27HdcJdwpSimulator::~HdcJdwpSimulator() {}
28
29void HdcJdwpSimulator::FinishWriteCallback(uv_write_t *req, int status)
30{
31    constexpr int bufSize = 1024;
32    char buf[bufSize] = { 0 };
33    uv_err_name_r(status, buf, bufSize);
34    HiLog::Info(LABEL, "FinishWriteCallback:%{public}d error:%{public}s", status, buf);
35    delete[](static_cast<uint8_t *>(req->data));
36    delete req;
37}
38
39RetErrCode HdcJdwpSimulator::SendToStream(uv_stream_t *handleStream, const uint8_t *buf,
40                                          const int bufLen, const void *finishCallback)
41{
42    HiLog::Info(LABEL, "HdcJdwpSimulator::SendToStream: %{public}s, %{public}d", buf, bufLen);
43    RetErrCode ret = RetErrCode::ERR_GENERIC;
44    if (bufLen <= 0) {
45        HiLog::Error(LABEL, "HdcJdwpSimulator::SendToStream wrong bufLen.");
46        return RetErrCode::ERR_GENERIC;
47    }
48    uint8_t *pDynBuf = new uint8_t[bufLen];
49    if (!pDynBuf) {
50        HiLog::Error(LABEL, "HdcJdwpSimulator::SendToStream new pDynBuf fail.");
51        return RetErrCode::ERR_GENERIC;
52    }
53    if (memcpy_s(pDynBuf, bufLen, buf, bufLen)) {
54        delete[] pDynBuf;
55        HiLog::Error(LABEL, "HdcJdwpSimulator::SendToStream memcpy fail.");
56        return RetErrCode::ERR_BUF_ALLOC;
57    }
58
59    uv_write_t *reqWrite = new uv_write_t();
60    if (!reqWrite) {
61        HiLog::Error(LABEL, "HdcJdwpSimulator::SendToStream alloc reqWrite fail.");
62        delete[] pDynBuf;
63        return RetErrCode::ERR_GENERIC;
64    }
65    uv_buf_t bfr;
66    while (true) {
67        reqWrite->data = static_cast<void *>(pDynBuf);
68        bfr.base = reinterpret_cast<char *>(pDynBuf);
69        bfr.len = bufLen;
70        if (!uv_is_writable(handleStream)) {
71            HiLog::Info(LABEL, "SendToStream uv_is_unwritable!");
72            delete[] pDynBuf;
73            delete reqWrite;
74            break;
75        }
76        HiLog::Info(LABEL, "SendToStream buf:%{public}s", pDynBuf);
77        uv_write(reqWrite, handleStream, &bfr, 1, (uv_write_cb)finishCallback);
78        ret = RetErrCode::SUCCESS;
79        break;
80    }
81    return ret;
82}
83
84void HdcJdwpSimulator::alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
85{
86    if (suggested_size <= 0) {
87        return;
88    }
89    buf->base = (char *)malloc(suggested_size);
90    buf->len = suggested_size;
91}
92
93#ifndef JS_JDWP_CONNECT
94// Process incoming data.  If no data is available, this will block until some
95// arrives.
96void HdcJdwpSimulator::ProcessIncoming(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
97{
98    HiLog::Debug(LABEL, "ProcessIncoming :%{public}d", nread);
99    do {
100        if (nread > 0) {
101            std::unique_ptr<char[]> recv = std::make_unique<char[]>(nread + 1);
102            if (memset_s(recv.get(), nread, 0, nread) != EOK) {
103                HiLog::Error(LABEL, "ProcessIncoming memset_s fail.");
104                break;
105            }
106            if (memcpy_s(recv.get(), nread, buf->base, nread) != EOK) {
107                HiLog::Error(LABEL, "ProcessIncoming memcpy_s fail.");
108                break;
109            }
110            for (int i = 0; i < (nread + 1); i++) {
111                HiLog::Info(LABEL, "ProcessIncoming recv2[%{public}d] :%{public}c", i, recv[i]);
112            }
113
114            vector<uint8_t> reply;
115            reply.clear();
116            reply.insert(reply.end(), HANDSHAKE_MESSAGE.c_str(),
117                         HANDSHAKE_MESSAGE.c_str() + HANDSHAKE_MESSAGE.size());
118            reply.insert(reply.end(), buf->base, buf->base + nread);
119            HiLog::Info(LABEL, "ProcessIncoming--reply server");
120            uint8_t *buf = reply.data();
121
122            for (int i = 0; i < (HANDSHAKE_MESSAGE.size() + nread + 1); i++) {
123                HiLog::Info(LABEL, "ProcessIncoming reply%{public}d :%{public}c", i, reply[i]);
124            }
125            SendToStream(client, buf, HANDSHAKE_MESSAGE.size() + nread + 1,
126                         (void *)FinishWriteCallback);
127        } else {
128            if (nread != UV_EOF) {
129                constexpr int bufSize = 1024;
130                char buffer[bufSize] = { 0 };
131                uv_err_name_r(nread, buffer, bufSize);
132                HiLog::Debug(LABEL, "ProcessIncoming error %s\n", buffer);
133            }
134            uv_close((uv_handle_t *)client, NULL);
135        }
136    } while (false);
137    free(buf->base);
138}
139
140// Get new socket fd passed from jdwp control
141void HdcJdwpSimulator::ReceiveNewFd(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf)
142{
143    HCtxJdwpSimulator ctxJdwp = static_cast<HCtxJdwpSimulator>(q->data);
144    HdcJdwpSimulator *thisClass = static_cast<HdcJdwpSimulator *>(ctxJdwp->thisClass);
145    int pidCurr = static_cast<int>(getpid());
146    HiLog::Debug(LABEL, "HdcJdwpSimulator::ReceiveNewFd pid: %{public}d, nread: %{public}d\n",
147                 pidCurr, nread);
148    if (nread < 0) {
149        if (nread != UV_EOF) {
150            constexpr int bufSize = 1024;
151            char buffer[bufSize] = { 0 };
152            uv_err_name_r(nread, buffer, bufSize);
153            HiLog::Error(LABEL, "Read error %s\n", buffer);
154        }
155        uv_close((uv_handle_t *)q, NULL);
156        return;
157    }
158
159    uv_pipe_t *pipe = reinterpret_cast<uv_pipe_t *>(q);
160    if (!uv_pipe_pending_count(pipe)) {
161        HiLog::Error(LABEL, "No pending count\n");
162        return;
163    }
164    uv_handle_type pending = uv_pipe_pending_type(pipe);
165    if (pending != UV_TCP) {
166        HiLog::Debug(LABEL, "None TCP type: %{public}d", pending);
167    }
168    uv_tcp_init(thisClass->loop, &ctxJdwp->newFd);
169    if (uv_accept(q, reinterpret_cast<uv_stream_t *>(&ctxJdwp->newFd)) == 0) {
170        uv_os_fd_t fd;
171        ctxJdwp->hasNewFd = true;
172        uv_fileno(reinterpret_cast<const uv_handle_t *>(&ctxJdwp->newFd), &fd);
173        HiLog::Debug(LABEL, "Jdwp forward pid %{public}d: new fd %{public}d\n", getpid(), fd);
174        uv_read_start(reinterpret_cast<uv_stream_t *>(&ctxJdwp->newFd), alloc_buffer,
175                      ProcessIncoming);
176    } else {
177        ctxJdwp->hasNewFd = false;
178        uv_close(reinterpret_cast<uv_handle_t *>(&ctxJdwp->newFd), NULL);
179    }
180}
181#endif // JS_JDWP_CONNECT
182
183void HdcJdwpSimulator::ConnectJdwp(uv_connect_t *connection, int status)
184{
185    constexpr int bufSize = 1024;
186    char buf[bufSize] = { 0 };
187    uv_err_name_r(status, buf, bufSize);
188    HiLog::Debug(LABEL, "ConnectJdwp:%{public}d error:%{public}s", status, buf);
189    uint32_t pidCurr = static_cast<uint32_t>(getpid());
190    HCtxJdwpSimulator ctxJdwp = static_cast<HCtxJdwpSimulator>(connection->data);
191    HdcJdwpSimulator *thisClass = static_cast<HdcJdwpSimulator *>(ctxJdwp->thisClass);
192    delete connection;
193
194#ifdef JS_JDWP_CONNECT
195    string pkgName = thisClass->pkgName;
196    uint32_t pkgSize = pkgName.size() + sizeof(JsMsgHeader); // JsMsgHeader pkgName;
197    uint8_t *info = new uint8_t[pkgSize]();
198    if (!info) {
199        HiLog::Error(LABEL, "ConnectJdwp new info fail.");
200        return;
201    }
202    do {
203        if (memset_s(info, pkgSize, 0, pkgSize) != EOK) {
204            HiLog::Error(LABEL, "ConnectJdwp memset_s fail.");
205            break;
206        }
207        JsMsgHeader *jsMsg = reinterpret_cast<JsMsgHeader *>(info);
208        jsMsg->pid = pidCurr;
209        jsMsg->msgLen = pkgSize;
210        HiLog::Info(LABEL,
211                    "ConnectJdwp send pid:%{public}d, pkgName:%{public}s, msgLen:%{public}d,",
212                    jsMsg->pid, pkgName.c_str(), jsMsg->msgLen);
213        bool retFail = false;
214        if (memcpy_s(info + sizeof(JsMsgHeader), pkgName.size(), &pkgName[0], pkgName.size()) !=
215            EOK) {
216            HiLog::Error(LABEL, "ConnectJdwp memcpy_s fail :%{public}s.", pkgName.c_str());
217            retFail = true;
218        }
219        if (!retFail) {
220            HiLog::Info(LABEL, "ConnectJdwp send JS msg:%{public}s", info);
221            thisClass->SendToStream(reinterpret_cast<uv_stream_t *>(&ctxJdwp->pipe), info, pkgSize,
222                                    (void *)FinishWriteCallback);
223        }
224    } while (false);
225    if (info) {
226        delete[] info;
227        info = nullptr;
228    }
229#else
230    int pidLength = 5;
231    char pid[pidLength] = {0};
232    if (to_string(pidCurr).length() >= pidLength || sprintf_s(pid, sizeof(pid), "%d", pidCurr) < 0) {
233        HiLog::Info(LABEL, "ConnectJdwp trans pid fail :%{public}d.", pidCurr);
234        return;
235    }
236    HiLog::Info(LABEL, "ConnectJdwp send pid:%{public}s", pid);
237    thisClass->SendToStream(reinterpret_cast<uv_stream_t *>(&ctxJdwp->pipe),
238                            reinterpret_cast<uint8_t *>(pid), sizeof(pidCurr),
239                            (void *)FinishWriteCallback);
240    HiLog::Info(LABEL, "ConnectJdwp reading.");
241    uv_read_start(reinterpret_cast<uv_stream_t *>(&ctxJdwp->pipe), thisClass->alloc_buffer,
242                  ReceiveNewFd);
243#endif // JS_JDWP_CONNECT
244}
245
246void *HdcJdwpSimulator::MallocContext()
247{
248    HCtxJdwpSimulator ctx = nullptr;
249    if ((ctx = new ContextJdwpSimulator()) == nullptr) {
250        return nullptr;
251    }
252    ctx->thisClass = this;
253    ctx->pipe.data = ctx;
254    ctx->hasNewFd = false;
255    return ctx;
256}
257
258void HdcJdwpSimulator::FreeContext()
259{
260    HiLog::Debug(LABEL, "HdcJdwpSimulator::FreeContext start");
261    if (!ctxPoint) {
262        return;
263    }
264    if (loop && !uv_is_closing(reinterpret_cast<uv_handle_t *>(&ctxPoint->pipe))) {
265        uv_close(reinterpret_cast<uv_handle_t *>(&ctxPoint->pipe), nullptr);
266    }
267    if (ctxPoint->hasNewFd && loop &&
268        !uv_is_closing(reinterpret_cast<uv_handle_t *>(&ctxPoint->newFd))) {
269        uv_close(reinterpret_cast<uv_handle_t *>(&ctxPoint->newFd), nullptr);
270    }
271    delete ctxPoint;
272    ctxPoint = nullptr;
273    HiLog::Debug(LABEL, "HdcJdwpSimulator::FreeContext end");
274}
275
276bool HdcJdwpSimulator::Connect()
277{
278    string jdwpCtrlName = "\0ohjpid-control";
279    uv_connect_t *connect = new uv_connect_t();
280    ctxPoint = static_cast<HCtxJdwpSimulator>(MallocContext());
281    if (!ctxPoint) {
282        HiLog::Info(LABEL, "MallocContext failed");
283        return false;
284    }
285    connect->data = ctxPoint;
286    uv_pipe_init(loop, static_cast<uv_pipe_t *>(&ctxPoint->pipe), 1);
287    HiLog::Info(LABEL, " HdcJdwpSimulator Connect begin");
288    uv_pipe_connect(connect, &ctxPoint->pipe, jdwpCtrlName.c_str(), ConnectJdwp);
289    return true;
290}
291
292void HdcJdwpSimulator::stop()
293{
294    HiLog::Debug(LABEL, "HdcJdwpSimulator::stop.");
295    FreeContext();
296}
297