blob: da2880a5e9e57d482ddeb2ca7e53a9d3473e0507 [file] [log] [blame]
David Pursell2ec418a2016-01-20 08:32:08 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include "tcp.h"
30
31#include <android-base/stringprintf.h>
32
33namespace tcp {
34
35static constexpr int kProtocolVersion = 1;
36static constexpr size_t kHandshakeLength = 4;
37static constexpr int kHandshakeTimeoutMs = 2000;
38
39// Extract the big-endian 8-byte message length into a 64-bit number.
40static uint64_t ExtractMessageLength(const void* buffer) {
41 uint64_t ret = 0;
42 for (int i = 0; i < 8; ++i) {
43 ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);
44 }
45 return ret;
46}
47
48// Encode the 64-bit number into a big-endian 8-byte message length.
49static void EncodeMessageLength(uint64_t length, void* buffer) {
50 for (int i = 0; i < 8; ++i) {
51 reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);
52 }
53}
54
55class TcpTransport : public Transport {
56 public:
57 // Factory function so we can return nullptr if initialization fails.
58 static std::unique_ptr<TcpTransport> NewTransport(std::unique_ptr<Socket> socket,
59 std::string* error);
60
61 ~TcpTransport() override = default;
62
63 ssize_t Read(void* data, size_t length) override;
64 ssize_t Write(const void* data, size_t length) override;
65 int Close() override;
66
67 private:
68 TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}
69
70 // Connects to the device and performs the initial handshake. Returns false and fills |error|
71 // on failure.
72 bool InitializeProtocol(std::string* error);
73
74 std::unique_ptr<Socket> socket_;
75 uint64_t message_bytes_left_ = 0;
76
77 DISALLOW_COPY_AND_ASSIGN(TcpTransport);
78};
79
80std::unique_ptr<TcpTransport> TcpTransport::NewTransport(std::unique_ptr<Socket> socket,
81 std::string* error) {
82 std::unique_ptr<TcpTransport> transport(new TcpTransport(std::move(socket)));
83
84 if (!transport->InitializeProtocol(error)) {
85 return nullptr;
86 }
87
88 return transport;
89}
90
91// These error strings are checked in tcp_test.cpp and should be kept in sync.
92bool TcpTransport::InitializeProtocol(std::string* error) {
93 std::string handshake_message(android::base::StringPrintf("FB%02d", kProtocolVersion));
94
95 if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {
96 *error = android::base::StringPrintf("Failed to send initialization message (%s)",
97 Socket::GetErrorMessage().c_str());
98 return false;
99 }
100
101 char buffer[kHandshakeLength];
102 if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
103 *error = android::base::StringPrintf(
104 "No initialization message received (%s). Target may not support TCP fastboot",
105 Socket::GetErrorMessage().c_str());
106 return false;
107 }
108
109 if (memcmp(buffer, "FB", 2) != 0) {
110 *error = "Unrecognized initialization message. Target may not support TCP fastboot";
111 return false;
112 }
113
114 if (memcmp(buffer + 2, "01", 2) != 0) {
115 *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
116 std::string(buffer + 2, 2).c_str(), kProtocolVersion);
117 return false;
118 }
119
120 error->clear();
121 return true;
122}
123
124ssize_t TcpTransport::Read(void* data, size_t length) {
125 if (socket_ == nullptr) {
126 return -1;
127 }
128
129 // Unless we're mid-message, read the next 8-byte message length.
130 if (message_bytes_left_ == 0) {
131 char buffer[8];
132 if (socket_->ReceiveAll(buffer, 8, 0) != 8) {
133 Close();
134 return -1;
135 }
136 message_bytes_left_ = ExtractMessageLength(buffer);
137 }
138
139 // Now read the message (up to |length| bytes).
140 if (length > message_bytes_left_) {
141 length = message_bytes_left_;
142 }
143 ssize_t bytes_read = socket_->ReceiveAll(data, length, 0);
144 if (bytes_read == -1) {
145 Close();
146 } else {
147 message_bytes_left_ -= bytes_read;
148 }
149 return bytes_read;
150}
151
152ssize_t TcpTransport::Write(const void* data, size_t length) {
153 if (socket_ == nullptr) {
154 return -1;
155 }
156
157 // Use multi-buffer writes for better performance.
158 char header[8];
159 EncodeMessageLength(length, header);
160 if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, length}})) {
161 Close();
162 return -1;
163 }
164
165 return length;
166}
167
168int TcpTransport::Close() {
169 if (socket_ == nullptr) {
170 return 0;
171 }
172
173 int result = socket_->Close();
174 socket_.reset();
175 return result;
176}
177
178std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
179 return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),
180 error);
181}
182
183namespace internal {
184
185std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
186 if (sock == nullptr) {
187 // If Socket creation failed |error| is already set.
188 return nullptr;
189 }
190
191 return TcpTransport::NewTransport(std::move(sock), error);
192}
193
194} // namespace internal
195
196} // namespace tcp