| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/host/gnubby_socket.h" |
| |
| #include "base/macros.h" |
| #include "base/timer/timer.h" |
| #include "net/socket/stream_listen_socket.h" |
| |
| namespace remoting { |
| |
| namespace { |
| |
| const size_t kRequestSizeBytes = 4; |
| const size_t kMaxRequestLength = 16384; |
| const unsigned int kRequestTimeoutSeconds = 60; |
| |
| // SSH Failure Code |
| const char kSshError[] = {0x05}; |
| |
| } // namespace |
| |
| GnubbySocket::GnubbySocket(scoped_ptr<net::StreamListenSocket> socket, |
| const base::Closure& timeout_callback) |
| : socket_(socket.Pass()) { |
| timer_.reset(new base::Timer(false, false)); |
| timer_->Start(FROM_HERE, |
| base::TimeDelta::FromSeconds(kRequestTimeoutSeconds), |
| timeout_callback); |
| } |
| |
| GnubbySocket::~GnubbySocket() {} |
| |
| void GnubbySocket::AddRequestData(const char* data, int data_len) { |
| DCHECK(CalledOnValidThread()); |
| |
| request_data_.insert(request_data_.end(), data, data + data_len); |
| ResetTimer(); |
| } |
| |
| void GnubbySocket::GetAndClearRequestData(std::string* data_out) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(IsRequestComplete() && !IsRequestTooLarge()); |
| |
| // The request size is not part of the data; don't send it. |
| data_out->assign(request_data_.begin() + kRequestSizeBytes, |
| request_data_.end()); |
| request_data_.clear(); |
| } |
| |
| bool GnubbySocket::IsRequestComplete() const { |
| DCHECK(CalledOnValidThread()); |
| |
| if (request_data_.size() < kRequestSizeBytes) |
| return false; |
| return GetRequestLength() <= request_data_.size(); |
| } |
| |
| bool GnubbySocket::IsRequestTooLarge() const { |
| DCHECK(CalledOnValidThread()); |
| |
| if (request_data_.size() < kRequestSizeBytes) |
| return false; |
| return GetRequestLength() > kMaxRequestLength; |
| } |
| |
| void GnubbySocket::SendResponse(const std::string& response_data) { |
| DCHECK(CalledOnValidThread()); |
| |
| socket_->Send(GetResponseLengthAsBytes(response_data)); |
| socket_->Send(response_data); |
| ResetTimer(); |
| } |
| |
| void GnubbySocket::SendSshError() { |
| DCHECK(CalledOnValidThread()); |
| |
| SendResponse(std::string(kSshError, arraysize(kSshError))); |
| } |
| |
| bool GnubbySocket::IsSocket(net::StreamListenSocket* socket) const { |
| return socket == socket_.get(); |
| } |
| |
| void GnubbySocket::SetTimerForTesting(scoped_ptr<base::Timer> timer) { |
| timer->Start(FROM_HERE, timer_->GetCurrentDelay(), timer_->user_task()); |
| timer_ = timer.Pass(); |
| } |
| |
| size_t GnubbySocket::GetRequestLength() const { |
| DCHECK(request_data_.size() >= kRequestSizeBytes); |
| |
| return ((request_data_[0] & 255) << 24) + ((request_data_[1] & 255) << 16) + |
| ((request_data_[2] & 255) << 8) + (request_data_[3] & 255) + |
| kRequestSizeBytes; |
| } |
| |
| std::string GnubbySocket::GetResponseLengthAsBytes( |
| const std::string& response) const { |
| std::string response_len; |
| int len = response.size(); |
| |
| response_len.push_back((len >> 24) & 255); |
| response_len.push_back((len >> 16) & 255); |
| response_len.push_back((len >> 8) & 255); |
| response_len.push_back(len & 255); |
| |
| return response_len; |
| } |
| |
| void GnubbySocket::ResetTimer() { |
| if (timer_->IsRunning()) |
| timer_->Reset(); |
| } |
| |
| } // namespace remoting |