summaryrefslogtreecommitdiffstats
path: root/remoting/host/gnubby_socket.cc
blob: 40cb7d42a63b72d1eb0a3bd551f29b25690bc87e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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