sync_socket_win.cc 11.7 KB
Newer Older
1
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/sync_socket.h"
6

7 8 9
#include <limits.h>
#include <stddef.h>

10
#include "base/logging.h"
11
#include "base/macros.h"
12
#include "base/rand_util.h"
13
#include "base/threading/thread_restrictions.h"
14
#include "base/win/scoped_handle.h"
15 16 17

namespace base {

18 19
using win::ScopedHandle;

20
namespace {
21 22 23 24 25
// IMPORTANT: do not change how this name is generated because it will break
// in sandboxed scenarios as we might have by-name policies that allow pipe
// creation. Also keep the secure random number generation.
const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
const size_t kPipePathMax =  arraysize(kPipeNameFormat) + (3 * 10) + 1;
26 27 28 29 30 31 32 33 34

// To avoid users sending negative message lengths to Send/Receive
// we clamp message lengths, which are size_t, to no more than INT_MAX.
const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);

const int kOutBufferSize = 4096;
const int kInBufferSize = 4096;
const int kDefaultTimeoutMilliSeconds = 1000;

35
bool CreatePairImpl(HANDLE* socket_a, HANDLE* socket_b, bool overlapped) {
36 37 38
  DCHECK_NE(socket_a, socket_b);
  DCHECK_EQ(*socket_a, SyncSocket::kInvalidHandle);
  DCHECK_EQ(*socket_b, SyncSocket::kInvalidHandle);
39 40

  wchar_t name[kPipePathMax];
41 42 43 44 45
  ScopedHandle handle_a;
  DWORD flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE;
  if (overlapped)
    flags |= FILE_FLAG_OVERLAPPED;

46
  do {
47 48
    unsigned long rnd_name;
    RandBytes(&rnd_name, sizeof(rnd_name));
49

50 51 52 53
    swprintf(name, kPipePathMax,
             kPipeNameFormat,
             GetCurrentProcessId(),
             GetCurrentThreadId(),
54
             rnd_name);
55 56

    handle_a.Set(CreateNamedPipeW(
57
        name,
58
        flags,
59 60 61 62 63
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
        1,
        kOutBufferSize,
        kInBufferSize,
        kDefaultTimeoutMilliSeconds,
64 65
        NULL));
  } while (!handle_a.IsValid() &&
66 67
           (GetLastError() == ERROR_PIPE_BUSY));

68
  if (!handle_a.IsValid()) {
69 70 71
    NOTREACHED();
    return false;
  }
72 73 74

  // The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot
  // impersonate the client (handle_b). This allows us not to care which side
75
  // ends up in which side of a privilege boundary.
76 77 78 79 80 81 82 83 84 85 86 87 88
  flags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
  if (overlapped)
    flags |= FILE_FLAG_OVERLAPPED;

  ScopedHandle handle_b(CreateFileW(name,
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,          // no sharing.
                                    NULL,       // default security attributes.
                                    OPEN_EXISTING,  // opens existing pipe.
                                    flags,
                                    NULL));     // no template file.
  if (!handle_b.IsValid()) {
    DPLOG(ERROR) << "CreateFileW failed";
89 90
    return false;
  }
91

92
  if (!ConnectNamedPipe(handle_a.Get(), NULL)) {
93 94
    DWORD error = GetLastError();
    if (error != ERROR_PIPE_CONNECTED) {
95
      DPLOG(ERROR) << "ConnectNamedPipe failed";
96 97 98
      return false;
    }
  }
99 100 101 102

  *socket_a = handle_a.Take();
  *socket_b = handle_b.Take();

103 104 105
  return true;
}

106 107 108 109 110 111 112 113 114 115 116 117
// Inline helper to avoid having the cast everywhere.
DWORD GetNextChunkSize(size_t current_pos, size_t max_size) {
  // The following statement is for 64 bit portability.
  return static_cast<DWORD>(((max_size - current_pos) <= UINT_MAX) ?
      (max_size - current_pos) : UINT_MAX);
}

// Template function that supports calling ReadFile or WriteFile in an
// overlapped fashion and waits for IO completion.  The function also waits
// on an event that can be used to cancel the operation.  If the operation
// is cancelled, the function returns and closes the relevant socket object.
template <typename BufferType, typename Function>
118 119 120 121 122 123
size_t CancelableFileOperation(Function operation,
                               HANDLE file,
                               BufferType* buffer,
                               size_t length,
                               WaitableEvent* io_event,
                               WaitableEvent* cancel_event,
xians@chromium.org's avatar
xians@chromium.org committed
124 125
                               CancelableSyncSocket* socket,
                               DWORD timeout_in_ms) {
126
  ThreadRestrictions::AssertIOAllowed();
127
  // The buffer must be byte size or the length check won't make much sense.
avi's avatar
avi committed
128
  static_assert(sizeof(buffer[0]) == sizeof(char), "incorrect buffer type");
129
  DCHECK_GT(length, 0u);
130
  DCHECK_LE(length, kMaxMessageLength);
131 132 133 134 135 136 137 138 139
  DCHECK_NE(file, SyncSocket::kInvalidHandle);

  // Track the finish time so we can calculate the timeout as data is read.
  TimeTicks current_time, finish_time;
  if (timeout_in_ms != INFINITE) {
    current_time = TimeTicks::Now();
    finish_time =
        current_time + base::TimeDelta::FromMilliseconds(timeout_in_ms);
  }
140 141

  size_t count = 0;
142 143 144 145 146 147
  do {
    // The OVERLAPPED structure will be modified by ReadFile or WriteFile.
    OVERLAPPED ol = { 0 };
    ol.hEvent = io_event->handle();

    const DWORD chunk = GetNextChunkSize(count, length);
148 149
    // This is either the ReadFile or WriteFile call depending on whether
    // we're receiving or sending data.
150
    DWORD len = 0;
151 152 153
    const BOOL operation_ok = operation(
        file, static_cast<BufferType*>(buffer) + count, chunk, &len, &ol);
    if (!operation_ok) {
154
      if (::GetLastError() == ERROR_IO_PENDING) {
xians@chromium.org's avatar
xians@chromium.org committed
155
        HANDLE events[] = { io_event->handle(), cancel_event->handle() };
156
        const int wait_result = WaitForMultipleObjects(
157
            arraysize(events), events, FALSE,
158 159 160 161
            timeout_in_ms == INFINITE ?
                timeout_in_ms :
                static_cast<DWORD>(
                    (finish_time - current_time).InMilliseconds()));
162 163 164 165
        if (wait_result != WAIT_OBJECT_0 + 0) {
          // CancelIo() doesn't synchronously cancel outstanding IO, only marks
          // outstanding IO for cancellation. We must call GetOverlappedResult()
          // below to ensure in flight writes complete before returning.
166
          CancelIo(file);
167 168 169 170 171 172 173 174 175
        }

        // We set the |bWait| parameter to TRUE for GetOverlappedResult() to
        // ensure writes are complete before returning.
        if (!GetOverlappedResult(file, &ol, &len, TRUE))
          len = 0;

        if (wait_result == WAIT_OBJECT_0 + 1) {
          DVLOG(1) << "Shutdown was signaled. Closing socket.";
176
          socket->Close();
177
          return count;
178
        }
179 180 181 182

        // Timeouts will be handled by the while() condition below since
        // GetOverlappedResult() may complete successfully after CancelIo().
        DCHECK(wait_result == WAIT_OBJECT_0 + 0 || wait_result == WAIT_TIMEOUT);
183
      } else {
xians@chromium.org's avatar
xians@chromium.org committed
184
        break;
185 186
      }
    }
xians@chromium.org's avatar
xians@chromium.org committed
187

188
    count += len;
xians@chromium.org's avatar
xians@chromium.org committed
189 190 191 192 193

    // Quit the operation if we can't write/read anymore.
    if (len != chunk)
      break;

194 195 196 197 198 199 200 201
    // Since TimeTicks::Now() is expensive, only bother updating the time if we
    // have more work to do.
    if (timeout_in_ms != INFINITE && count < length)
      current_time = base::TimeTicks::Now();
  } while (count < length &&
           (timeout_in_ms == INFINITE || current_time < finish_time));

  return count;
202 203 204 205
}

}  // namespace

206
#if defined(COMPONENT_BUILD)
207
const SyncSocket::Handle SyncSocket::kInvalidHandle = INVALID_HANDLE_VALUE;
208
#endif
209 210 211 212 213 214 215 216 217 218 219 220

SyncSocket::SyncSocket() : handle_(kInvalidHandle) {}

SyncSocket::~SyncSocket() {
  Close();
}

// static
bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
  return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, false);
}

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
// static
SyncSocket::Handle SyncSocket::UnwrapHandle(
    const TransitDescriptor& descriptor) {
  return descriptor;
}

bool SyncSocket::PrepareTransitDescriptor(ProcessHandle peer_process_handle,
                                          TransitDescriptor* descriptor) {
  DCHECK(descriptor);
  if (!::DuplicateHandle(GetCurrentProcess(), handle(), peer_process_handle,
                         descriptor, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
    DPLOG(ERROR) << "Cannot duplicate socket handle for peer process.";
    return false;
  }
  return true;
}

238
bool SyncSocket::Close() {
239
  if (handle_ == kInvalidHandle)
240
    return true;
241

242
  const BOOL result = CloseHandle(handle_);
243
  handle_ = kInvalidHandle;
244
  return result == TRUE;
245 246 247
}

size_t SyncSocket::Send(const void* buffer, size_t length) {
248 249
  ThreadRestrictions::AssertIOAllowed();
  DCHECK_GT(length, 0u);
250
  DCHECK_LE(length, kMaxMessageLength);
251
  DCHECK_NE(handle_, kInvalidHandle);
252 253 254
  size_t count = 0;
  while (count < length) {
    DWORD len;
255
    DWORD chunk = GetNextChunkSize(count, length);
256 257
    if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
                  chunk, &len, NULL) == FALSE) {
258
      return count;
259 260 261 262 263 264
    }
    count += len;
  }
  return count;
}

265 266 267 268 269 270 271
size_t SyncSocket::ReceiveWithTimeout(void* buffer,
                                      size_t length,
                                      TimeDelta timeout) {
  NOTIMPLEMENTED();
  return 0;
}

272
size_t SyncSocket::Receive(void* buffer, size_t length) {
273 274
  ThreadRestrictions::AssertIOAllowed();
  DCHECK_GT(length, 0u);
275
  DCHECK_LE(length, kMaxMessageLength);
276
  DCHECK_NE(handle_, kInvalidHandle);
277 278 279
  size_t count = 0;
  while (count < length) {
    DWORD len;
280
    DWORD chunk = GetNextChunkSize(count, length);
281 282
    if (ReadFile(handle_, static_cast<char*>(buffer) + count,
                 chunk, &len, NULL) == FALSE) {
283
      return count;
284 285 286 287 288 289
    }
    count += len;
  }
  return count;
}

290 291 292 293 294 295
size_t SyncSocket::Peek() {
  DWORD available = 0;
  PeekNamedPipe(handle_, NULL, 0, NULL, &available, NULL);
  return available;
}

296 297 298 299 300 301
SyncSocket::Handle SyncSocket::Release() {
  Handle r = handle_;
  handle_ = kInvalidHandle;
  return r;
}

302
CancelableSyncSocket::CancelableSyncSocket()
303 304 305 306
    : shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
                      base::WaitableEvent::InitialState::NOT_SIGNALED),
      file_operation_(base::WaitableEvent::ResetPolicy::MANUAL,
                      base::WaitableEvent::InitialState::NOT_SIGNALED) {}
307 308

CancelableSyncSocket::CancelableSyncSocket(Handle handle)
309 310 311 312 313
    : SyncSocket(handle),
      shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
                      base::WaitableEvent::InitialState::NOT_SIGNALED),
      file_operation_(base::WaitableEvent::ResetPolicy::MANUAL,
                      base::WaitableEvent::InitialState::NOT_SIGNALED) {}
314 315 316 317 318 319 320 321 322

bool CancelableSyncSocket::Shutdown() {
  // This doesn't shut down the pipe immediately, but subsequent Receive or Send
  // methods will fail straight away.
  shutdown_event_.Signal();
  return true;
}

bool CancelableSyncSocket::Close() {
323
  const bool result = SyncSocket::Close();
324
  shutdown_event_.Reset();
325
  return result;
326 327 328
}

size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
xians@chromium.org's avatar
xians@chromium.org committed
329 330 331 332
  static const DWORD kWaitTimeOutInMs = 500;
  return CancelableFileOperation(
      &WriteFile, handle_, reinterpret_cast<const char*>(buffer),
      length, &file_operation_, &shutdown_event_, this, kWaitTimeOutInMs);
333 334 335
}

size_t CancelableSyncSocket::Receive(void* buffer, size_t length) {
336 337 338 339 340 341 342 343 344 345
  return CancelableFileOperation(
      &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
      &file_operation_, &shutdown_event_, this, INFINITE);
}

size_t CancelableSyncSocket::ReceiveWithTimeout(void* buffer,
                                                size_t length,
                                                TimeDelta timeout) {
  return CancelableFileOperation(
      &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
346 347
      &file_operation_, &shutdown_event_, this,
      static_cast<DWORD>(timeout.InMilliseconds()));
348 349 350 351 352 353 354 355
}

// static
bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
                                      CancelableSyncSocket* socket_b) {
  return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true);
}

356
}  // namespace base