• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
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 "net/ssl/ssl_key_logger_impl.h"
6 
7 #include <stdio.h>
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/functional/bind.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/sequence_checker.h"
19 #include "base/synchronization/lock.h"
20 #include "base/task/sequenced_task_runner.h"
21 #include "base/task/task_traits.h"
22 #include "base/task/thread_pool.h"
23 #include "base/thread_annotations.h"
24 
25 namespace net {
26 
27 namespace {
28 // Bound the number of outstanding writes to bound memory usage. Some
29 // antiviruses point this at a pipe and then read too slowly. See
30 // https://crbug.com/566951 and https://crbug.com/914880.
31 static constexpr size_t kMaxOutstandingLines = 512;
32 }  // namespace
33 
34 // An object which performs the blocking file operations on a background
35 // SequencedTaskRunner.
36 class SSLKeyLoggerImpl::Core
37     : public base::RefCountedThreadSafe<SSLKeyLoggerImpl::Core> {
38  public:
Core()39   Core() {
40     DETACH_FROM_SEQUENCE(sequence_checker_);
41     // That the user explicitly asked for debugging information would suggest
42     // waiting to flush these to disk, but some buggy antiviruses point this at
43     // a pipe and hang, so we avoid blocking shutdown. If writing to a real
44     // file, writes should complete quickly enough that this does not matter.
45     task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
46         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
47   }
48 
49   Core(const Core&) = delete;
50   Core& operator=(const Core&) = delete;
51 
SetFile(base::File file)52   void SetFile(base::File file) {
53     file_.reset(base::FileToFILE(std::move(file), "a"));
54     if (!file_)
55       DVLOG(1) << "Could not adopt file";
56   }
57 
OpenFile(const base::FilePath & path)58   void OpenFile(const base::FilePath& path) {
59     task_runner_->PostTask(FROM_HERE,
60                            base::BindOnce(&Core::OpenFileImpl, this, path));
61   }
62 
WriteLine(const std::string & line)63   void WriteLine(const std::string& line) {
64     bool was_empty;
65     {
66       base::AutoLock lock(lock_);
67       was_empty = buffer_.empty();
68       if (buffer_.size() < kMaxOutstandingLines) {
69         buffer_.push_back(line);
70       } else {
71         lines_dropped_ = true;
72       }
73     }
74     if (was_empty) {
75       task_runner_->PostTask(FROM_HERE, base::BindOnce(&Core::Flush, this));
76     }
77   }
78 
79  private:
80   friend class base::RefCountedThreadSafe<Core>;
81   ~Core() = default;
82 
OpenFileImpl(const base::FilePath & path)83   void OpenFileImpl(const base::FilePath& path) {
84     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85     DCHECK(!file_);
86     file_.reset(base::OpenFile(path, "a"));
87     if (!file_)
88       DVLOG(1) << "Could not open " << path.value();
89   }
90 
Flush()91   void Flush() {
92     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
93 
94     bool lines_dropped = false;
95     std::vector<std::string> buffer;
96     {
97       base::AutoLock lock(lock_);
98       std::swap(lines_dropped, lines_dropped_);
99       std::swap(buffer, buffer_);
100     }
101 
102     if (file_) {
103       for (const auto& line : buffer) {
104         fprintf(file_.get(), "%s\n", line.c_str());
105       }
106       if (lines_dropped) {
107         fprintf(file_.get(), "# Some lines were dropped due to slow writes.\n");
108       }
109       fflush(file_.get());
110     }
111   }
112 
113   scoped_refptr<base::SequencedTaskRunner> task_runner_;
114   base::ScopedFILE file_;
115   SEQUENCE_CHECKER(sequence_checker_);
116 
117   base::Lock lock_;
118   bool lines_dropped_ GUARDED_BY(lock_) = false;
119   std::vector<std::string> buffer_ GUARDED_BY(lock_);
120 };
121 
SSLKeyLoggerImpl(const base::FilePath & path)122 SSLKeyLoggerImpl::SSLKeyLoggerImpl(const base::FilePath& path)
123     : core_(base::MakeRefCounted<Core>()) {
124   core_->OpenFile(path);
125 }
126 
SSLKeyLoggerImpl(base::File file)127 SSLKeyLoggerImpl::SSLKeyLoggerImpl(base::File file)
128     : core_(base::MakeRefCounted<Core>()) {
129   core_->SetFile(std::move(file));
130 }
131 
132 SSLKeyLoggerImpl::~SSLKeyLoggerImpl() = default;
133 
WriteLine(const std::string & line)134 void SSLKeyLoggerImpl::WriteLine(const std::string& line) {
135   core_->WriteLine(line);
136 }
137 
138 }  // namespace net
139