diff options
Diffstat (limited to 'host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp')
-rw-r--r-- | host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp b/host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp new file mode 100644 index 000000000..dcb6d503d --- /dev/null +++ b/host/frontend/gcastv2/webrtc/AdbWebSocketHandler.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <webrtc/AdbWebSocketHandler.h> + +#include "Utils.h" + +#include <https/BaseConnection.h> +#include <https/Support.h> + +#include <android-base/logging.h> + +#include <unistd.h> + +using namespace android; + +struct AdbWebSocketHandler::AdbConnection : public BaseConnection { + explicit AdbConnection( + AdbWebSocketHandler *parent, + std::shared_ptr<RunLoop> runLoop, + int sock); + + void send(const void *_data, size_t size); + +protected: + ssize_t processClientRequest(const void *data, size_t size) override; + void onDisconnect(int err) override; + +private: + AdbWebSocketHandler *mParent; +}; + +//////////////////////////////////////////////////////////////////////////////// + +AdbWebSocketHandler::AdbConnection::AdbConnection( + AdbWebSocketHandler *parent, + std::shared_ptr<RunLoop> runLoop, + int sock) + : BaseConnection(runLoop, sock), + mParent(parent) { +} + +// Thanks for calling it a crc32, adb documentation! +static uint32_t computeNotACrc32(const void *_data, size_t size) { + auto data = static_cast<const uint8_t *>(_data); + uint32_t sum = 0; + for (size_t i = 0; i < size; ++i) { + sum += data[i]; + } + + return sum; +} + +static int verifyAdbHeader( + const void *_data, size_t size, size_t *_payloadLength) { + auto data = static_cast<const uint8_t *>(_data); + + *_payloadLength = 0; + + if (size < 24) { + return -EAGAIN; + } + + uint32_t command = U32LE_AT(data); + uint32_t magic = U32LE_AT(data + 20); + + if (command != (magic ^ 0xffffffff)) { + return -EINVAL; + } + + uint32_t payloadLength = U32LE_AT(data + 12); + + if (size < 24 + payloadLength) { + return -EAGAIN; + } + + auto payloadCrc = U32LE_AT(data + 16); + auto crc32 = computeNotACrc32(data + 24, payloadLength); + + if (payloadCrc != crc32) { + return -EINVAL; + } + + *_payloadLength = payloadLength; + + return 0; +} + +ssize_t AdbWebSocketHandler::AdbConnection::processClientRequest( + const void *_data, size_t size) { + auto data = static_cast<const uint8_t *>(_data); + + LOG(VERBOSE) + << "AdbConnection::processClientRequest (size = " << size << ")"; + + // hexdump(data, size); + + size_t payloadLength; + int err = verifyAdbHeader(data, size, &payloadLength); + + if (err) { + return err; + } + + mParent->sendMessage( + data, payloadLength + 24, WebSocketHandler::SendMode::binary); + + return payloadLength + 24; +} + +void AdbWebSocketHandler::AdbConnection::onDisconnect(int err) { + LOG(INFO) << "AdbConnection::onDisconnect(err=" << err << ")"; + + mParent->sendMessage( + nullptr /* data */, + 0 /* size */, + WebSocketHandler::SendMode::closeConnection); +} + +void AdbWebSocketHandler::AdbConnection::send(const void *_data, size_t size) { + BaseConnection::send(_data, size); +} + +//////////////////////////////////////////////////////////////////////////////// + +AdbWebSocketHandler::AdbWebSocketHandler( + std::shared_ptr<RunLoop> runLoop, + const std::string &adb_host_and_port) + : mRunLoop(runLoop), + mSocket(-1) { + LOG(INFO) << "Connecting to " << adb_host_and_port; + + auto err = setupSocket(adb_host_and_port); + CHECK(!err); + + mAdbConnection = std::make_shared<AdbConnection>(this, mRunLoop, mSocket); +} + +AdbWebSocketHandler::~AdbWebSocketHandler() { + if (mSocket >= 0) { + close(mSocket); + mSocket = -1; + } +} + +void AdbWebSocketHandler::run() { + mAdbConnection->run(); +} + +int AdbWebSocketHandler::setupSocket(const std::string &adb_host_and_port) { + auto colonPos = adb_host_and_port.find(':'); + if (colonPos == std::string::npos) { + return -EINVAL; + } + + auto host = adb_host_and_port.substr(0, colonPos); + + const char *portString = adb_host_and_port.c_str() + colonPos + 1; + char *end; + unsigned long port = strtoul(portString, &end, 10); + + if (end == portString || *end != '\0' || port > 65535) { + return -EINVAL; + } + + int err; + + int sock = socket(PF_INET, SOCK_STREAM, 0); + + if (sock < 0) { + err = -errno; + goto bail; + } + + makeFdNonblocking(sock); + + sockaddr_in addr; + memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(host.c_str()); + addr.sin_port = htons(port); + + if (connect(sock, + reinterpret_cast<const sockaddr *>(&addr), + sizeof(addr)) < 0 + && errno != EINPROGRESS) { + err = -errno; + goto bail2; + } + + mSocket = sock; + + return 0; + +bail2: + close(sock); + sock = -1; + +bail: + return err; +} + +int AdbWebSocketHandler::handleMessage( + uint8_t headerByte, const uint8_t *msg, size_t len) { + LOG(VERBOSE) + << "headerByte = " + << StringPrintf("0x%02x", (unsigned)headerByte); + + // hexdump(msg, len); + + if (!(headerByte & 0x80)) { + // I only want to receive whole messages here, not fragments. + return -EINVAL; + } + + auto opcode = headerByte & 0x1f; + switch (opcode) { + case 0x8: + { + // closeConnection. + break; + } + + case 0x2: + { + // binary + + size_t payloadLength; + int err = verifyAdbHeader(msg, len, &payloadLength); + + if (err || len != 24 + payloadLength) { + LOG(ERROR) << "websocket message is not a valid adb message."; + return -EINVAL; + } + + mAdbConnection->send(msg, len); + break; + } + + default: + return -EINVAL; + } + + return 0; +} + |