Home | History | Annotate | Download | only in host
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/gnubby_socket.h"
      6 
      7 #include "base/macros.h"
      8 #include "base/timer/timer.h"
      9 #include "net/socket/stream_listen_socket.h"
     10 
     11 namespace remoting {
     12 
     13 namespace {
     14 
     15 const size_t kRequestSizeBytes = 4;
     16 const size_t kMaxRequestLength = 16384;
     17 const unsigned int kRequestTimeoutSeconds = 60;
     18 
     19 // SSH Failure Code
     20 const char kSshError[] = {0x05};
     21 
     22 }  // namespace
     23 
     24 GnubbySocket::GnubbySocket(scoped_ptr<net::StreamListenSocket> socket,
     25                            const base::Closure& timeout_callback)
     26     : socket_(socket.Pass()) {
     27   timer_.reset(new base::Timer(false, false));
     28   timer_->Start(FROM_HERE,
     29                 base::TimeDelta::FromSeconds(kRequestTimeoutSeconds),
     30                 timeout_callback);
     31 }
     32 
     33 GnubbySocket::~GnubbySocket() {}
     34 
     35 void GnubbySocket::AddRequestData(const char* data, int data_len) {
     36   DCHECK(CalledOnValidThread());
     37 
     38   request_data_.insert(request_data_.end(), data, data + data_len);
     39   ResetTimer();
     40 }
     41 
     42 void GnubbySocket::GetAndClearRequestData(std::string* data_out) {
     43   DCHECK(CalledOnValidThread());
     44   DCHECK(IsRequestComplete() && !IsRequestTooLarge());
     45 
     46   // The request size is not part of the data; don't send it.
     47   data_out->assign(request_data_.begin() + kRequestSizeBytes,
     48                    request_data_.end());
     49   request_data_.clear();
     50 }
     51 
     52 bool GnubbySocket::IsRequestComplete() const {
     53   DCHECK(CalledOnValidThread());
     54 
     55   if (request_data_.size() < kRequestSizeBytes)
     56     return false;
     57   return GetRequestLength() <= request_data_.size();
     58 }
     59 
     60 bool GnubbySocket::IsRequestTooLarge() const {
     61   DCHECK(CalledOnValidThread());
     62 
     63   if (request_data_.size() < kRequestSizeBytes)
     64     return false;
     65   return GetRequestLength() > kMaxRequestLength;
     66 }
     67 
     68 void GnubbySocket::SendResponse(const std::string& response_data) {
     69   DCHECK(CalledOnValidThread());
     70 
     71   socket_->Send(GetResponseLengthAsBytes(response_data));
     72   socket_->Send(response_data);
     73   ResetTimer();
     74 }
     75 
     76 void GnubbySocket::SendSshError() {
     77   DCHECK(CalledOnValidThread());
     78 
     79   SendResponse(std::string(kSshError, arraysize(kSshError)));
     80 }
     81 
     82 bool GnubbySocket::IsSocket(net::StreamListenSocket* socket) const {
     83   return socket == socket_.get();
     84 }
     85 
     86 void GnubbySocket::SetTimerForTesting(scoped_ptr<base::Timer> timer) {
     87   timer->Start(FROM_HERE, timer_->GetCurrentDelay(), timer_->user_task());
     88   timer_ = timer.Pass();
     89 }
     90 
     91 size_t GnubbySocket::GetRequestLength() const {
     92   DCHECK(request_data_.size() >= kRequestSizeBytes);
     93 
     94   return ((request_data_[0] & 255) << 24) + ((request_data_[1] & 255) << 16) +
     95          ((request_data_[2] & 255) << 8) + (request_data_[3] & 255) +
     96          kRequestSizeBytes;
     97 }
     98 
     99 std::string GnubbySocket::GetResponseLengthAsBytes(
    100     const std::string& response) const {
    101   std::string response_len;
    102   int len = response.size();
    103 
    104   response_len.push_back((len >> 24) & 255);
    105   response_len.push_back((len >> 16) & 255);
    106   response_len.push_back((len >> 8) & 255);
    107   response_len.push_back(len & 255);
    108 
    109   return response_len;
    110 }
    111 
    112 void GnubbySocket::ResetTimer() {
    113   if (timer_->IsRunning())
    114     timer_->Reset();
    115 }
    116 
    117 }  // namespace remoting
    118