blob: 032e04f7d6b0b940d01fce7672670c666fc6783d [file] [log] [blame]
// Copyright (c) 2012 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 "base/sync_socket.h"
#include <limits.h>
#include <stdio.h>
#include <windows.h>
#include <sys/types.h>
#include "base/logging.h"
namespace base {
namespace {
// 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;
// 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;
} // namespace
const SyncSocket::Handle SyncSocket::kInvalidHandle = INVALID_HANDLE_VALUE;
bool SyncSocket::CreatePair(SyncSocket* pair[2]) {
Handle handles[2];
SyncSocket* tmp_sockets[2];
// Create the two SyncSocket objects first to avoid ugly cleanup issues.
tmp_sockets[0] = new SyncSocket(kInvalidHandle);
if (tmp_sockets[0] == NULL) {
return false;
}
tmp_sockets[1] = new SyncSocket(kInvalidHandle);
if (tmp_sockets[1] == NULL) {
delete tmp_sockets[0];
return false;
}
wchar_t name[kPipePathMax];
do {
unsigned int rnd_name;
if (rand_s(&rnd_name) != 0)
return false;
swprintf(name, kPipePathMax,
kPipeNameFormat,
GetCurrentProcessId(),
GetCurrentThreadId(),
rnd_name);
handles[0] = CreateNamedPipeW(
name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
1,
kOutBufferSize,
kInBufferSize,
kDefaultTimeoutMilliSeconds,
NULL);
} while ((handles[0] == INVALID_HANDLE_VALUE) &&
(GetLastError() == ERROR_PIPE_BUSY));
if (handles[0] == INVALID_HANDLE_VALUE) {
NOTREACHED();
return false;
}
// The SECURITY_ANONYMOUS flag means that the server side (pair[0]) cannot
// impersonate the client (pair[1]). This allows us not to care which side
// ends up in which side of a privilege boundary.
handles[1] = CreateFileW(name,
GENERIC_READ | GENERIC_WRITE,
0, // no sharing.
NULL, // default security attributes.
OPEN_EXISTING, // opens existing pipe.
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
NULL); // no template file.
if (handles[1] == INVALID_HANDLE_VALUE) {
CloseHandle(handles[0]);
return false;
}
if (ConnectNamedPipe(handles[0], NULL) == FALSE) {
DWORD error = GetLastError();
if (error != ERROR_PIPE_CONNECTED) {
CloseHandle(handles[0]);
CloseHandle(handles[1]);
return false;
}
}
// Copy the handles out for successful return.
tmp_sockets[0]->handle_ = handles[0];
pair[0] = tmp_sockets[0];
tmp_sockets[1]->handle_ = handles[1];
pair[1] = tmp_sockets[1];
return true;
}
bool SyncSocket::Close() {
if (handle_ == kInvalidHandle) {
return false;
}
BOOL retval = CloseHandle(handle_);
handle_ = kInvalidHandle;
return retval ? true : false;
}
size_t SyncSocket::Send(const void* buffer, size_t length) {
DCHECK_LE(length, kMaxMessageLength);
size_t count = 0;
while (count < length) {
DWORD len;
// The following statement is for 64 bit portability.
DWORD chunk = static_cast<DWORD>(
((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
chunk, &len, NULL) == FALSE) {
return (0 < count) ? count : 0;
}
count += len;
}
return count;
}
size_t SyncSocket::Receive(void* buffer, size_t length) {
DCHECK_LE(length, kMaxMessageLength);
size_t count = 0;
while (count < length) {
DWORD len;
DWORD chunk = static_cast<DWORD>(
((length - count) <= UINT_MAX) ? (length - count) : UINT_MAX);
if (ReadFile(handle_, static_cast<char*>(buffer) + count,
chunk, &len, NULL) == FALSE) {
return (0 < count) ? count : 0;
}
count += len;
}
return count;
}
size_t SyncSocket::Peek() {
DWORD available = 0;
PeekNamedPipe(handle_, NULL, 0, NULL, &available, NULL);
return available;
}
} // namespace base