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
11namespace remoting {
12
13namespace {
14
15const size_t kRequestSizeBytes = 4;
16const size_t kMaxRequestLength = 16384;
17const unsigned int kRequestTimeoutSeconds = 60;
18
19// SSH Failure Code
20const char kSshError[] = {0x05};
21
22}  // namespace
23
24GnubbySocket::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
33GnubbySocket::~GnubbySocket() {}
34
35void 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
42void 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
52bool GnubbySocket::IsRequestComplete() const {
53  DCHECK(CalledOnValidThread());
54
55  if (request_data_.size() < kRequestSizeBytes)
56    return false;
57  return GetRequestLength() <= request_data_.size();
58}
59
60bool GnubbySocket::IsRequestTooLarge() const {
61  DCHECK(CalledOnValidThread());
62
63  if (request_data_.size() < kRequestSizeBytes)
64    return false;
65  return GetRequestLength() > kMaxRequestLength;
66}
67
68void 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
76void GnubbySocket::SendSshError() {
77  DCHECK(CalledOnValidThread());
78
79  SendResponse(std::string(kSshError, arraysize(kSshError)));
80}
81
82bool GnubbySocket::IsSocket(net::StreamListenSocket* socket) const {
83  return socket == socket_.get();
84}
85
86void GnubbySocket::SetTimerForTesting(scoped_ptr<base::Timer> timer) {
87  timer->Start(FROM_HERE, timer_->GetCurrentDelay(), timer_->user_task());
88  timer_ = timer.Pass();
89}
90
91size_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
99std::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
112void GnubbySocket::ResetTimer() {
113  if (timer_->IsRunning())
114    timer_->Reset();
115}
116
117}  // namespace remoting
118