1// Copyright (c) 2012 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 "base/sync_socket.h"
6
7#include "base/logging.h"
8#include "base/threading/thread_restrictions.h"
9#include "base/win/scoped_handle.h"
10
11namespace base {
12
13using win::ScopedHandle;
14
15namespace {
16// IMPORTANT: do not change how this name is generated because it will break
17// in sandboxed scenarios as we might have by-name policies that allow pipe
18// creation. Also keep the secure random number generation.
19const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
20const size_t kPipePathMax =  arraysize(kPipeNameFormat) + (3 * 10) + 1;
21
22// To avoid users sending negative message lengths to Send/Receive
23// we clamp message lengths, which are size_t, to no more than INT_MAX.
24const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
25
26const int kOutBufferSize = 4096;
27const int kInBufferSize = 4096;
28const int kDefaultTimeoutMilliSeconds = 1000;
29
30bool CreatePairImpl(HANDLE* socket_a, HANDLE* socket_b, bool overlapped) {
31  DCHECK_NE(socket_a, socket_b);
32  DCHECK_EQ(*socket_a, SyncSocket::kInvalidHandle);
33  DCHECK_EQ(*socket_b, SyncSocket::kInvalidHandle);
34
35  wchar_t name[kPipePathMax];
36  ScopedHandle handle_a;
37  DWORD flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE;
38  if (overlapped)
39    flags |= FILE_FLAG_OVERLAPPED;
40
41  do {
42    unsigned int rnd_name;
43    if (rand_s(&rnd_name) != 0)
44      return false;
45
46    swprintf(name, kPipePathMax,
47             kPipeNameFormat,
48             GetCurrentProcessId(),
49             GetCurrentThreadId(),
50             rnd_name);
51
52    handle_a.Set(CreateNamedPipeW(
53        name,
54        flags,
55        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
56        1,
57        kOutBufferSize,
58        kInBufferSize,
59        kDefaultTimeoutMilliSeconds,
60        NULL));
61  } while (!handle_a.IsValid() &&
62           (GetLastError() == ERROR_PIPE_BUSY));
63
64  if (!handle_a.IsValid()) {
65    NOTREACHED();
66    return false;
67  }
68
69  // The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot
70  // impersonate the client (handle_b). This allows us not to care which side
71  // ends up in which side of a privilege boundary.
72  flags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
73  if (overlapped)
74    flags |= FILE_FLAG_OVERLAPPED;
75
76  ScopedHandle handle_b(CreateFileW(name,
77                                    GENERIC_READ | GENERIC_WRITE,
78                                    0,          // no sharing.
79                                    NULL,       // default security attributes.
80                                    OPEN_EXISTING,  // opens existing pipe.
81                                    flags,
82                                    NULL));     // no template file.
83  if (!handle_b.IsValid()) {
84    DPLOG(ERROR) << "CreateFileW failed";
85    return false;
86  }
87
88  if (!ConnectNamedPipe(handle_a, NULL)) {
89    DWORD error = GetLastError();
90    if (error != ERROR_PIPE_CONNECTED) {
91      DPLOG(ERROR) << "ConnectNamedPipe failed";
92      return false;
93    }
94  }
95
96  *socket_a = handle_a.Take();
97  *socket_b = handle_b.Take();
98
99  return true;
100}
101
102// Inline helper to avoid having the cast everywhere.
103DWORD GetNextChunkSize(size_t current_pos, size_t max_size) {
104  // The following statement is for 64 bit portability.
105  return static_cast<DWORD>(((max_size - current_pos) <= UINT_MAX) ?
106      (max_size - current_pos) : UINT_MAX);
107}
108
109// Template function that supports calling ReadFile or WriteFile in an
110// overlapped fashion and waits for IO completion.  The function also waits
111// on an event that can be used to cancel the operation.  If the operation
112// is cancelled, the function returns and closes the relevant socket object.
113template <typename BufferType, typename Function>
114size_t CancelableFileOperation(Function operation,
115                               HANDLE file,
116                               BufferType* buffer,
117                               size_t length,
118                               WaitableEvent* io_event,
119                               WaitableEvent* cancel_event,
120                               CancelableSyncSocket* socket,
121                               DWORD timeout_in_ms) {
122  ThreadRestrictions::AssertIOAllowed();
123  // The buffer must be byte size or the length check won't make much sense.
124  COMPILE_ASSERT(sizeof(buffer[0]) == sizeof(char), incorrect_buffer_type);
125  DCHECK_GT(length, 0u);
126  DCHECK_LE(length, kMaxMessageLength);
127  DCHECK_NE(file, SyncSocket::kInvalidHandle);
128
129  // Track the finish time so we can calculate the timeout as data is read.
130  TimeTicks current_time, finish_time;
131  if (timeout_in_ms != INFINITE) {
132    current_time = TimeTicks::Now();
133    finish_time =
134        current_time + base::TimeDelta::FromMilliseconds(timeout_in_ms);
135  }
136
137  size_t count = 0;
138  do {
139    // The OVERLAPPED structure will be modified by ReadFile or WriteFile.
140    OVERLAPPED ol = { 0 };
141    ol.hEvent = io_event->handle();
142
143    const DWORD chunk = GetNextChunkSize(count, length);
144    // This is either the ReadFile or WriteFile call depending on whether
145    // we're receiving or sending data.
146    DWORD len = 0;
147    const BOOL operation_ok = operation(
148        file, static_cast<BufferType*>(buffer) + count, chunk, &len, &ol);
149    if (!operation_ok) {
150      if (::GetLastError() == ERROR_IO_PENDING) {
151        HANDLE events[] = { io_event->handle(), cancel_event->handle() };
152        const int wait_result = WaitForMultipleObjects(
153            ARRAYSIZE_UNSAFE(events), events, FALSE,
154            timeout_in_ms == INFINITE
155                ? timeout_in_ms
156                : (finish_time - current_time).InMilliseconds());
157        if (wait_result == (WAIT_OBJECT_0 + 0)) {
158          GetOverlappedResult(file, &ol, &len, TRUE);
159        } else if (wait_result == (WAIT_OBJECT_0 + 1)) {
160          DVLOG(1) << "Shutdown was signaled. Closing socket.";
161          CancelIo(file);
162          socket->Close();
163          count = 0;
164          break;
165        } else {
166          // Timeout happened.
167          DCHECK_EQ(WAIT_TIMEOUT, wait_result);
168          if (!CancelIo(file))
169            DLOG(WARNING) << "CancelIo() failed";
170          break;
171        }
172      } else {
173        break;
174      }
175    }
176
177    count += len;
178
179    // Quit the operation if we can't write/read anymore.
180    if (len != chunk)
181      break;
182
183    // Since TimeTicks::Now() is expensive, only bother updating the time if we
184    // have more work to do.
185    if (timeout_in_ms != INFINITE && count < length)
186      current_time = base::TimeTicks::Now();
187  } while (count < length &&
188           (timeout_in_ms == INFINITE || current_time < finish_time));
189
190  return count;
191}
192
193}  // namespace
194
195#if defined(COMPONENT_BUILD)
196const SyncSocket::Handle SyncSocket::kInvalidHandle = INVALID_HANDLE_VALUE;
197#endif
198
199SyncSocket::SyncSocket() : handle_(kInvalidHandle) {}
200
201SyncSocket::~SyncSocket() {
202  Close();
203}
204
205// static
206bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
207  return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, false);
208}
209
210bool SyncSocket::Close() {
211  if (handle_ == kInvalidHandle)
212    return true;
213
214  const BOOL result = CloseHandle(handle_);
215  handle_ = kInvalidHandle;
216  return result == TRUE;
217}
218
219size_t SyncSocket::Send(const void* buffer, size_t length) {
220  ThreadRestrictions::AssertIOAllowed();
221  DCHECK_GT(length, 0u);
222  DCHECK_LE(length, kMaxMessageLength);
223  DCHECK_NE(handle_, kInvalidHandle);
224  size_t count = 0;
225  while (count < length) {
226    DWORD len;
227    DWORD chunk = GetNextChunkSize(count, length);
228    if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
229                  chunk, &len, NULL) == FALSE) {
230      return count;
231    }
232    count += len;
233  }
234  return count;
235}
236
237size_t SyncSocket::ReceiveWithTimeout(void* buffer,
238                                      size_t length,
239                                      TimeDelta timeout) {
240  NOTIMPLEMENTED();
241  return 0;
242}
243
244size_t SyncSocket::Receive(void* buffer, size_t length) {
245  ThreadRestrictions::AssertIOAllowed();
246  DCHECK_GT(length, 0u);
247  DCHECK_LE(length, kMaxMessageLength);
248  DCHECK_NE(handle_, kInvalidHandle);
249  size_t count = 0;
250  while (count < length) {
251    DWORD len;
252    DWORD chunk = GetNextChunkSize(count, length);
253    if (ReadFile(handle_, static_cast<char*>(buffer) + count,
254                 chunk, &len, NULL) == FALSE) {
255      return count;
256    }
257    count += len;
258  }
259  return count;
260}
261
262size_t SyncSocket::Peek() {
263  DWORD available = 0;
264  PeekNamedPipe(handle_, NULL, 0, NULL, &available, NULL);
265  return available;
266}
267
268CancelableSyncSocket::CancelableSyncSocket()
269    : shutdown_event_(true, false), file_operation_(true, false) {
270}
271
272CancelableSyncSocket::CancelableSyncSocket(Handle handle)
273    : SyncSocket(handle), shutdown_event_(true, false),
274      file_operation_(true, false) {
275}
276
277bool CancelableSyncSocket::Shutdown() {
278  // This doesn't shut down the pipe immediately, but subsequent Receive or Send
279  // methods will fail straight away.
280  shutdown_event_.Signal();
281  return true;
282}
283
284bool CancelableSyncSocket::Close() {
285  const bool result = SyncSocket::Close();
286  shutdown_event_.Reset();
287  return result;
288}
289
290size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
291  static const DWORD kWaitTimeOutInMs = 500;
292  return CancelableFileOperation(
293      &WriteFile, handle_, reinterpret_cast<const char*>(buffer),
294      length, &file_operation_, &shutdown_event_, this, kWaitTimeOutInMs);
295}
296
297size_t CancelableSyncSocket::Receive(void* buffer, size_t length) {
298  return CancelableFileOperation(
299      &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
300      &file_operation_, &shutdown_event_, this, INFINITE);
301}
302
303size_t CancelableSyncSocket::ReceiveWithTimeout(void* buffer,
304                                                size_t length,
305                                                TimeDelta timeout) {
306  return CancelableFileOperation(
307      &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
308      &file_operation_, &shutdown_event_, this, timeout.InMilliseconds());
309}
310
311// static
312bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
313                                      CancelableSyncSocket* socket_b) {
314  return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true);
315}
316
317}  // namespace base
318