<commit_before>// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unordered_set>
#include <vector>

#include "gtest/gtest.h"
#include "leveldb/env.h"
#include "port/port.h"
#include "util/env_posix_test_helper.h"
#include "util/testutil.h"

#if HAVE_O_CLOEXEC

namespace {

// Exit codes for the helper process spawned by TestCloseOnExec* tests.
// Useful for debugging test failures.
constexpr int kTextCloseOnExecHelperExecFailedCode = 61;
constexpr int kTextCloseOnExecHelperDup2FailedCode = 62;
constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63;

// Global set by main() and read in TestCloseOnExec.
//
// The argv[0] value is stored in a std::vector instead of a std::string because
// std::string does not return a mutable pointer to its buffer until C++17.
//
// The vector stores the string pointed to by argv[0], plus the trailing null.
std::vector<char>* GetArgvZero() {
  static std::vector<char> program_name;
  return &program_name;
}

// Command-line switch used to run this test as the CloseOnExecSwitch helper.
static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper";

// Executed in a separate process by TestCloseOnExec* tests.
//
// main() delegates to this function when the test executable is launched with
// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
// executable and pass the special command-line switch.
//

// main() delegates to this function when the test executable is launched with
// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
// executable and pass the special command-line switch.
//
// When main() delegates to this function, the process probes whether a given
// file descriptor is open, and communicates the result via its exit code.
int TestCloseOnExecHelperMain(char* pid_arg) {
  int fd = std::atoi(pid_arg);
  // When given the same file descriptor twice, dup2() returns -1 if the
  // file descriptor is closed, or the given file descriptor if it is open.
  if (::dup2(fd, fd) == fd) {
    std::fprintf(stderr, "Unexpected open fd %d\n", fd);
    return kTextCloseOnExecHelperFoundOpenFdCode;
  }
  // Double-check that dup2() is saying the file descriptor is closed.
  if (errno != EBADF) {
    std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n",
                 fd, std::strerror(errno));
    return kTextCloseOnExecHelperDup2FailedCode;
  }
  return 0;
}

// File descriptors are small non-negative integers.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetMaxFileDescriptor(int* result_fd) {
  // Get the maximum file descriptor number.
  ::rlimit fd_rlimit;
  ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit));
  *result_fd = fd_rlimit.rlim_cur;
}

// Iterates through all possible FDs and returns the currently open ones.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetOpenFileDescriptors(std::unordered_set<int>* open_fds) {
  int max_fd = 0;
  GetMaxFileDescriptor(&max_fd);

  for (int fd = 0; fd < max_fd; ++fd) {
    if (::dup2(fd, fd) != fd) {
      // When given the same file descriptor twice, dup2() returns -1 if the
      // file descriptor is closed, or the given file descriptor if it is open.
      //
      // Double-check that dup2() is saying the fd is closed.
      ASSERT_EQ(EBADF, errno)
          << "dup2() should set errno to EBADF on closed file descriptors";
      continue;
    }
    open_fds->insert(fd);
  }
}

// Finds an FD open since a previous call to GetOpenFileDescriptors().
//
// |baseline_open_fds| is the result of a previous GetOpenFileDescriptors()
// call. Assumes that exactly one FD was opened since that call.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetNewlyOpenedFileDescriptor(
    const std::unordered_set<int>& baseline_open_fds, int* result_fd) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);
  for (int fd : baseline_open_fds) {
    ASSERT_EQ(1, open_fds.count(fd))
        << "Previously opened file descriptor was closed during test setup";
    open_fds.erase(fd);
  }
  ASSERT_EQ(1, open_fds.size())
      << "Expected exactly one newly opened file descriptor during test setup";
  *result_fd = *open_fds.begin();
}

// Check that a fork()+exec()-ed child process does not have an extra open FD.
void CheckCloseOnExecDoesNotLeakFDs(
    const std::unordered_set<int>& baseline_open_fds) {
  // Prepare the argument list for the child process.
  // execv() wants mutable buffers.
  char switch_buffer[sizeof(kTestCloseOnExecSwitch)];
  std::memcpy(switch_buffer, kTestCloseOnExecSwitch,
              sizeof(kTestCloseOnExecSwitch));

  int probed_fd;
  GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd);
  std::string fd_string = std::to_string(probed_fd);
  std::vector<char> fd_buffer(fd_string.begin(), fd_string.end());
  fd_buffer.emplace_back('\0');

  // The helper process is launched with the command below.
  //      env_posix_tests --test-close-on-exec-helper 3
  char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(),
                        nullptr};

  constexpr int kForkInChildProcessReturnValue = 0;
  int child_pid = fork();
  if (child_pid == kForkInChildProcessReturnValue) {
    ::execv(child_argv[0], child_argv);
    std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno));
    std::exit(kTextCloseOnExecHelperExecFailedCode);
  }

  int child_status = 0;
  ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0));
  ASSERT_TRUE(WIFEXITED(child_status))
      << "The helper process did not exit with an exit code";
  ASSERT_EQ(0, WEXITSTATUS(child_status))
      << "The helper process encountered an error";
}

}  // namespace

#endif  // HAVE_O_CLOEXEC

namespace leveldb {

static const int kReadOnlyFileLimit = 4;
static const int kMMapLimit = 4;

class EnvPosixTest : public testing::Test {
 public:
  static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
    EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
    EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);
  }

  EnvPosixTest() : env_(Env::Default()) {}

  Env* env_;
};

TEST_F(EnvPosixTest, TestOpenOnRead) {
  // Write some test data to a single file that will be opened |n| times.
  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string test_file = test_dir + "/open_on_read.txt";

  FILE* f = std::fopen(test_file.c_str(), "we");
  ASSERT_TRUE(f != nullptr);
  const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
  fputs(kFileData, f);
  std::fclose(f);

  // Open test file some number above the sum of the two limits to force
  // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
  const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
  leveldb::RandomAccessFile* files[kNumFiles] = {0};
  for (int i = 0; i < kNumFiles; i++) {
    ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
  }
  char scratch;
  Slice read_result;
  for (int i = 0; i < kNumFiles; i++) {
    ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
    ASSERT_EQ(kFileData[i], read_result[0]);
  }
  for (int i = 0; i < kNumFiles; i++) {
    delete files[i];
  }
  ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
}

#if HAVE_O_CLOEXEC

TEST_F(EnvPosixTest, TestCloseOnExecSequentialFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_sequential.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::SequentialFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewSequentialFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_random_access.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  // Exhaust the RandomAccessFile mmap limit. This way, the test
  // RandomAccessFile instance below is backed by a file descriptor, not by an
  // mmap region.
  leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr};
  for (int i = 0; i < kReadOnlyFileLimit; i++) {
    ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
  }

  leveldb::RandomAccessFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  for (int i = 0; i < kReadOnlyFileLimit; i++) {
    delete mmapped_files[i];
  }
  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecWritableFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_writable.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::WritableFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewWritableFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecAppendableFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_appendable.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::WritableFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewAppendableFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecLockFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_lock.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::FileLock* lock = nullptr;
  ASSERT_LEVELDB_OK(env_->LockFile(file_path, &lock));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  ASSERT_LEVELDB_OK(env_->UnlockFile(lock));

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecLogger) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_logger.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::Logger* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewLogger(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

#endif  // HAVE_O_CLOEXEC

}  // namespace leveldb

int main(int argc, char** argv) {
#if HAVE_O_CLOEXEC
  // Check if we're invoked as a helper program, or as the test suite.
  for (int i = 1; i < argc; ++i) {
    if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) {
      return TestCloseOnExecHelperMain(argv[i + 1]);
    }
  }

  // Save argv[0] early, because googletest may modify argv.
  GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1);
#endif  // HAVE_O_CLOEXEC

  // All tests currently run with the same read-only file limits.
  leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
                                       leveldb::kMMapLimit);

  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>fixed random access file exhaust random mmap file use wrong limit count<commit_after>// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unordered_set>
#include <vector>

#include "gtest/gtest.h"
#include "leveldb/env.h"
#include "port/port.h"
#include "util/env_posix_test_helper.h"
#include "util/testutil.h"

#if HAVE_O_CLOEXEC

namespace {

// Exit codes for the helper process spawned by TestCloseOnExec* tests.
// Useful for debugging test failures.
constexpr int kTextCloseOnExecHelperExecFailedCode = 61;
constexpr int kTextCloseOnExecHelperDup2FailedCode = 62;
constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63;

// Global set by main() and read in TestCloseOnExec.
//
// The argv[0] value is stored in a std::vector instead of a std::string because
// std::string does not return a mutable pointer to its buffer until C++17.
//
// The vector stores the string pointed to by argv[0], plus the trailing null.
std::vector<char>* GetArgvZero() {
  static std::vector<char> program_name;
  return &program_name;
}

// Command-line switch used to run this test as the CloseOnExecSwitch helper.
static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper";

// Executed in a separate process by TestCloseOnExec* tests.
//
// main() delegates to this function when the test executable is launched with
// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
// executable and pass the special command-line switch.
//

// main() delegates to this function when the test executable is launched with
// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
// executable and pass the special command-line switch.
//
// When main() delegates to this function, the process probes whether a given
// file descriptor is open, and communicates the result via its exit code.
int TestCloseOnExecHelperMain(char* pid_arg) {
  int fd = std::atoi(pid_arg);
  // When given the same file descriptor twice, dup2() returns -1 if the
  // file descriptor is closed, or the given file descriptor if it is open.
  if (::dup2(fd, fd) == fd) {
    std::fprintf(stderr, "Unexpected open fd %d\n", fd);
    return kTextCloseOnExecHelperFoundOpenFdCode;
  }
  // Double-check that dup2() is saying the file descriptor is closed.
  if (errno != EBADF) {
    std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n",
                 fd, std::strerror(errno));
    return kTextCloseOnExecHelperDup2FailedCode;
  }
  return 0;
}

// File descriptors are small non-negative integers.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetMaxFileDescriptor(int* result_fd) {
  // Get the maximum file descriptor number.
  ::rlimit fd_rlimit;
  ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit));
  *result_fd = fd_rlimit.rlim_cur;
}

// Iterates through all possible FDs and returns the currently open ones.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetOpenFileDescriptors(std::unordered_set<int>* open_fds) {
  int max_fd = 0;
  GetMaxFileDescriptor(&max_fd);

  for (int fd = 0; fd < max_fd; ++fd) {
    if (::dup2(fd, fd) != fd) {
      // When given the same file descriptor twice, dup2() returns -1 if the
      // file descriptor is closed, or the given file descriptor if it is open.
      //
      // Double-check that dup2() is saying the fd is closed.
      ASSERT_EQ(EBADF, errno)
          << "dup2() should set errno to EBADF on closed file descriptors";
      continue;
    }
    open_fds->insert(fd);
  }
}

// Finds an FD open since a previous call to GetOpenFileDescriptors().
//
// |baseline_open_fds| is the result of a previous GetOpenFileDescriptors()
// call. Assumes that exactly one FD was opened since that call.
//
// Returns void so the implementation can use ASSERT_EQ.
void GetNewlyOpenedFileDescriptor(
    const std::unordered_set<int>& baseline_open_fds, int* result_fd) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);
  for (int fd : baseline_open_fds) {
    ASSERT_EQ(1, open_fds.count(fd))
        << "Previously opened file descriptor was closed during test setup";
    open_fds.erase(fd);
  }
  ASSERT_EQ(1, open_fds.size())
      << "Expected exactly one newly opened file descriptor during test setup";
  *result_fd = *open_fds.begin();
}

// Check that a fork()+exec()-ed child process does not have an extra open FD.
void CheckCloseOnExecDoesNotLeakFDs(
    const std::unordered_set<int>& baseline_open_fds) {
  // Prepare the argument list for the child process.
  // execv() wants mutable buffers.
  char switch_buffer[sizeof(kTestCloseOnExecSwitch)];
  std::memcpy(switch_buffer, kTestCloseOnExecSwitch,
              sizeof(kTestCloseOnExecSwitch));

  int probed_fd;
  GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd);
  std::string fd_string = std::to_string(probed_fd);
  std::vector<char> fd_buffer(fd_string.begin(), fd_string.end());
  fd_buffer.emplace_back('\0');

  // The helper process is launched with the command below.
  //      env_posix_tests --test-close-on-exec-helper 3
  char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(),
                        nullptr};

  constexpr int kForkInChildProcessReturnValue = 0;
  int child_pid = fork();
  if (child_pid == kForkInChildProcessReturnValue) {
    ::execv(child_argv[0], child_argv);
    std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno));
    std::exit(kTextCloseOnExecHelperExecFailedCode);
  }

  int child_status = 0;
  ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0));
  ASSERT_TRUE(WIFEXITED(child_status))
      << "The helper process did not exit with an exit code";
  ASSERT_EQ(0, WEXITSTATUS(child_status))
      << "The helper process encountered an error";
}

}  // namespace

#endif  // HAVE_O_CLOEXEC

namespace leveldb {

static const int kReadOnlyFileLimit = 4;
static const int kMMapLimit = 4;

class EnvPosixTest : public testing::Test {
 public:
  static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
    EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
    EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);
  }

  EnvPosixTest() : env_(Env::Default()) {}

  Env* env_;
};

TEST_F(EnvPosixTest, TestOpenOnRead) {
  // Write some test data to a single file that will be opened |n| times.
  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string test_file = test_dir + "/open_on_read.txt";

  FILE* f = std::fopen(test_file.c_str(), "we");
  ASSERT_TRUE(f != nullptr);
  const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
  fputs(kFileData, f);
  std::fclose(f);

  // Open test file some number above the sum of the two limits to force
  // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
  const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
  leveldb::RandomAccessFile* files[kNumFiles] = {0};
  for (int i = 0; i < kNumFiles; i++) {
    ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
  }
  char scratch;
  Slice read_result;
  for (int i = 0; i < kNumFiles; i++) {
    ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
    ASSERT_EQ(kFileData[i], read_result[0]);
  }
  for (int i = 0; i < kNumFiles; i++) {
    delete files[i];
  }
  ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
}

#if HAVE_O_CLOEXEC

TEST_F(EnvPosixTest, TestCloseOnExecSequentialFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_sequential.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::SequentialFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewSequentialFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_random_access.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  // Exhaust the RandomAccessFile mmap limit. This way, the test
  // RandomAccessFile instance below is backed by a file descriptor, not by an
  // mmap region.
  leveldb::RandomAccessFile* mmapped_files[kMMapLimit];
  for (int i = 0; i < kMMapLimit; i++) {
    ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
  }

  leveldb::RandomAccessFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  for (int i = 0; i < kMMapLimit; i++) {
    delete mmapped_files[i];
  }
  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecWritableFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_writable.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::WritableFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewWritableFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecAppendableFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_appendable.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::WritableFile* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewAppendableFile(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecLockFile) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_lock.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::FileLock* lock = nullptr;
  ASSERT_LEVELDB_OK(env_->LockFile(file_path, &lock));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  ASSERT_LEVELDB_OK(env_->UnlockFile(lock));

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

TEST_F(EnvPosixTest, TestCloseOnExecLogger) {
  std::unordered_set<int> open_fds;
  GetOpenFileDescriptors(&open_fds);

  std::string test_dir;
  ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  std::string file_path = test_dir + "/close_on_exec_logger.txt";
  ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));

  leveldb::Logger* file = nullptr;
  ASSERT_LEVELDB_OK(env_->NewLogger(file_path, &file));
  CheckCloseOnExecDoesNotLeakFDs(open_fds);
  delete file;

  ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}

#endif  // HAVE_O_CLOEXEC

}  // namespace leveldb

int main(int argc, char** argv) {
#if HAVE_O_CLOEXEC
  // Check if we're invoked as a helper program, or as the test suite.
  for (int i = 1; i < argc; ++i) {
    if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) {
      return TestCloseOnExecHelperMain(argv[i + 1]);
    }
  }

  // Save argv[0] early, because googletest may modify argv.
  GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1);
#endif  // HAVE_O_CLOEXEC

  // All tests currently run with the same read-only file limits.
  leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
                                       leveldb::kMMapLimit);

  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef INTEGER_RANGE_HPP
#define INTEGER_RANGE_HPP

#include <type_traits>

namespace osrm
{

template <typename Integer> class range
{
  private:
    Integer last;
    Integer iter;

  public:
    range(Integer start, Integer end) : last(end), iter(start)
    {
        static_assert(std::is_integral<Integer>::value, "range type must be integral");
    }

    // Iterable functions
    const range &begin() const { return *this; }
    const range &end() const { return *this; }
    Integer front() const { return iter; }
    Integer back() const { return last - 1; }

    // Iterator functions
    bool operator!=(const range &) const { return iter < last; }
    void operator++() { ++iter; }
    Integer operator*() const { return iter; }
};

// convenience function to construct an integer range with type deduction
template <typename Integer>
range<Integer> irange(const Integer first,
                      const Integer last,
                      typename std::enable_if<std::is_integral<Integer>::value>::type * = 0)
{
    return range<Integer>(first, last);
}
}

#endif // INTEGER_RANGE_HPP
<commit_msg>add size() call to integer range<commit_after>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef INTEGER_RANGE_HPP
#define INTEGER_RANGE_HPP

#include <type_traits>

namespace osrm
{

template <typename Integer> class range
{
  private:
    Integer last;
    Integer iter;

  public:
    range(Integer start, Integer end) : last(end), iter(start)
    {
        static_assert(std::is_integral<Integer>::value, "range type must be integral");
    }

    // Iterable functions
    const range &begin() const { return *this; }
    const range &end() const { return *this; }
    Integer front() const { return iter; }
    Integer back() const { return last - 1; }
    Integer size() const { return last - iter; }

    // Iterator functions
    bool operator!=(const range &) const { return iter < last; }
    void operator++() { ++iter; }
    Integer operator*() const { return iter; }
};

// convenience function to construct an integer range with type deduction
template <typename Integer>
range<Integer> irange(const Integer first,
                      const Integer last,
                      typename std::enable_if<std::is_integral<Integer>::value>::type * = 0)
{
    return range<Integer>(first, last);
}
}

#endif // INTEGER_RANGE_HPP
<|endoftext|>


<commit_before>#pragma once

#include <rai/secure.hpp>

#include <atomic>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_set>

namespace rai
{
class work_pool
{
public:
	work_pool ();
	~work_pool ();
	void loop (uint64_t);
	void stop ();
	uint64_t generate (rai::uint256_union const &);
	void generate (rai::block &);
	uint64_t work_value (rai::block_hash const &, uint64_t);
	bool work_validate (rai::block &);
	bool work_validate (rai::block_hash const &, uint64_t);
	rai::uint256_union current;
	std::atomic <int> ticket;
	bool done;
	std::vector <std::thread> threads;
	std::unordered_map <rai::uint256_union, uint64_t> completed;
	std::queue <rai::uint256_union> pending;
	std::mutex mutex;
	std::condition_variable consumer_condition;
	std::condition_variable producer_condition;
	// Local work threshold for rate-limiting publishing blocks. ~5 seconds of work.
	static uint64_t const publish_test_threshold = 0xff00000000000000;
	static uint64_t const publish_full_threshold = 0xffffffc000000000;
	static uint64_t const publish_threshold = rai::rai_network == rai::rai_networks::rai_test_network ? publish_test_threshold : publish_full_threshold;
};
// The fan spreads a key out over the heap to decrease the likelyhood of it being recovered by memory inspection
class fan
{
public:
    fan (rai::uint256_union const &, size_t);
    rai::uint256_union value ();
    void value_set (rai::uint256_union const &);
    std::vector <std::unique_ptr <rai::uint256_union>> values;
};
class wallet_value
{
public:
	wallet_value () = default;
	wallet_value (MDB_val const &);
	wallet_value (rai::uint256_union const &);
	rai::mdb_val val () const;
	rai::private_key key;
	uint64_t work;
};
class node_config;
class wallet_store
{
public:
    wallet_store (bool &, rai::transaction &, rai::account, std::string const &);
    wallet_store (bool &, rai::transaction &, rai::account, std::string const &, std::string const &);
	std::vector <rai::account> accounts (MDB_txn *);
    void initialize (MDB_txn *, bool &, std::string const &);
    rai::uint256_union check (MDB_txn *);
    bool rekey (MDB_txn *, std::string const &);
    bool valid_password (MDB_txn *);
    bool attempt_password (MDB_txn *, std::string const &);
    rai::uint256_union wallet_key (MDB_txn *);
    rai::uint256_union salt (MDB_txn *);
    bool is_representative (MDB_txn *);
    rai::account representative (MDB_txn *);
    void representative_set (MDB_txn *, rai::account const &);
    rai::public_key insert (MDB_txn *, rai::private_key const &);
    void erase (MDB_txn *, rai::public_key const &);
	rai::wallet_value entry_get_raw (MDB_txn *, rai::public_key const &);
	void entry_put_raw (MDB_txn *, rai::public_key const &, rai::wallet_value const &);
    bool fetch (MDB_txn *, rai::public_key const &, rai::private_key &);
    bool exists (MDB_txn *, rai::public_key const &);
	void destroy (MDB_txn *);
    rai::store_iterator find (MDB_txn *, rai::uint256_union const &);
    rai::store_iterator begin (MDB_txn *);
    rai::store_iterator end ();
    rai::uint256_union derive_key (MDB_txn *, std::string const &);
    void serialize_json (MDB_txn *, std::string &);
	void write_backup (MDB_txn *, boost::filesystem::path const &);
    bool move (MDB_txn *, rai::wallet_store &, std::vector <rai::public_key> const &);
	bool import (MDB_txn *, rai::wallet_store &);
	bool work_get (MDB_txn *, rai::public_key const &, uint64_t &);
	void work_put (MDB_txn *, rai::public_key const &, uint64_t);
    rai::fan password;
    static rai::uint256_union const version_1;
    static rai::uint256_union const version_current;
    static rai::uint256_union const version_special;
    static rai::uint256_union const wallet_key_special;
    static rai::uint256_union const salt_special;
    static rai::uint256_union const check_special;
    static rai::uint256_union const representative_special;
    static int const special_count;
    static unsigned const kdf_full_work = 1 * 1024 * 1024;
    static unsigned const kdf_test_work = 8;
    static unsigned const kdf_work = rai::rai_network == rai::rai_networks::rai_test_network ? kdf_test_work : kdf_full_work;
	rai::mdb_env & environment;
    MDB_dbi handle;
};
class node;
// A wallet is a set of account keys encrypted by a common encryption key
class wallet : public std::enable_shared_from_this <rai::wallet>
{
public:
    wallet (bool &, rai::transaction &, rai::node &, std::string const &);
    wallet (bool &, rai::transaction &, rai::node &, std::string const &, std::string const &);
	void enter_initial_password ();
    bool valid_password ();
    bool enter_password (std::string const &);
	rai::public_key insert (rai::private_key const &);
    bool exists (rai::public_key const &);
	bool import (std::string const &, std::string const &);
	void serialize (std::string &);
	bool change_action (rai::account const &, rai::account const &);
    bool receive_action (rai::send_block const &, rai::private_key const &, rai::account const &, rai::uint128_union const &);
	rai::block_hash send_action (rai::account const &, rai::account const &, rai::uint128_t const &);
	bool change_sync (rai::account const &, rai::account const &);
    bool receive_sync (rai::send_block const &, rai::private_key const &, rai::account const &, rai::uint128_t const &);
	rai::block_hash send_sync (rai::account const &, rai::account const &, rai::uint128_t const &);
    void work_generate (rai::account const &, rai::block_hash const &);
    void work_update (MDB_txn *, rai::account const &, rai::block_hash const &, uint64_t);
    uint64_t work_fetch (MDB_txn *, rai::account const &, rai::block_hash const &);
	bool search_pending ();
	std::function <void (bool, bool)> lock_observer;
    rai::wallet_store store;
    rai::node & node;
};
// The wallets set is all the wallets a node controls.  A node may contain multiple wallets independently encrypted and operated.
class wallets
{
public:
	wallets (bool &, rai::node &);
	std::shared_ptr <rai::wallet> open (rai::uint256_union const &);
	std::shared_ptr <rai::wallet> create (rai::uint256_union const &);
    bool search_pending (rai::uint256_union const &);
	void destroy (rai::uint256_union const &);
	void queue_wallet_action (rai::account const &, rai::uint128_t const &, std::function <void ()> const &);
	void foreach_representative (std::function <void (rai::public_key const &, rai::private_key const &)> const &);
	std::function <void (rai::account const &, bool)> observer;
	std::unordered_map <rai::uint256_union, std::shared_ptr <rai::wallet>> items;
	std::unordered_map <rai::account, std::multimap <rai::uint128_t, std::function <void ()>, std::greater <rai::uint128_t>>> pending_actions;
	std::unordered_set <rai::account> current_actions;
	std::mutex action_mutex;
	MDB_dbi handle;
	rai::node & node;
};
}<commit_msg>Tuning down the kdf parameter.<commit_after>#pragma once

#include <rai/secure.hpp>

#include <atomic>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_set>

namespace rai
{
class work_pool
{
public:
	work_pool ();
	~work_pool ();
	void loop (uint64_t);
	void stop ();
	uint64_t generate (rai::uint256_union const &);
	void generate (rai::block &);
	uint64_t work_value (rai::block_hash const &, uint64_t);
	bool work_validate (rai::block &);
	bool work_validate (rai::block_hash const &, uint64_t);
	rai::uint256_union current;
	std::atomic <int> ticket;
	bool done;
	std::vector <std::thread> threads;
	std::unordered_map <rai::uint256_union, uint64_t> completed;
	std::queue <rai::uint256_union> pending;
	std::mutex mutex;
	std::condition_variable consumer_condition;
	std::condition_variable producer_condition;
	// Local work threshold for rate-limiting publishing blocks. ~5 seconds of work.
	static uint64_t const publish_test_threshold = 0xff00000000000000;
	static uint64_t const publish_full_threshold = 0xffffffc000000000;
	static uint64_t const publish_threshold = rai::rai_network == rai::rai_networks::rai_test_network ? publish_test_threshold : publish_full_threshold;
};
// The fan spreads a key out over the heap to decrease the likelyhood of it being recovered by memory inspection
class fan
{
public:
    fan (rai::uint256_union const &, size_t);
    rai::uint256_union value ();
    void value_set (rai::uint256_union const &);
    std::vector <std::unique_ptr <rai::uint256_union>> values;
};
class wallet_value
{
public:
	wallet_value () = default;
	wallet_value (MDB_val const &);
	wallet_value (rai::uint256_union const &);
	rai::mdb_val val () const;
	rai::private_key key;
	uint64_t work;
};
class node_config;
class wallet_store
{
public:
    wallet_store (bool &, rai::transaction &, rai::account, std::string const &);
    wallet_store (bool &, rai::transaction &, rai::account, std::string const &, std::string const &);
	std::vector <rai::account> accounts (MDB_txn *);
    void initialize (MDB_txn *, bool &, std::string const &);
    rai::uint256_union check (MDB_txn *);
    bool rekey (MDB_txn *, std::string const &);
    bool valid_password (MDB_txn *);
    bool attempt_password (MDB_txn *, std::string const &);
    rai::uint256_union wallet_key (MDB_txn *);
    rai::uint256_union salt (MDB_txn *);
    bool is_representative (MDB_txn *);
    rai::account representative (MDB_txn *);
    void representative_set (MDB_txn *, rai::account const &);
    rai::public_key insert (MDB_txn *, rai::private_key const &);
    void erase (MDB_txn *, rai::public_key const &);
	rai::wallet_value entry_get_raw (MDB_txn *, rai::public_key const &);
	void entry_put_raw (MDB_txn *, rai::public_key const &, rai::wallet_value const &);
    bool fetch (MDB_txn *, rai::public_key const &, rai::private_key &);
    bool exists (MDB_txn *, rai::public_key const &);
	void destroy (MDB_txn *);
    rai::store_iterator find (MDB_txn *, rai::uint256_union const &);
    rai::store_iterator begin (MDB_txn *);
    rai::store_iterator end ();
    rai::uint256_union derive_key (MDB_txn *, std::string const &);
    void serialize_json (MDB_txn *, std::string &);
	void write_backup (MDB_txn *, boost::filesystem::path const &);
    bool move (MDB_txn *, rai::wallet_store &, std::vector <rai::public_key> const &);
	bool import (MDB_txn *, rai::wallet_store &);
	bool work_get (MDB_txn *, rai::public_key const &, uint64_t &);
	void work_put (MDB_txn *, rai::public_key const &, uint64_t);
    rai::fan password;
    static rai::uint256_union const version_1;
    static rai::uint256_union const version_current;
    static rai::uint256_union const version_special;
    static rai::uint256_union const wallet_key_special;
    static rai::uint256_union const salt_special;
    static rai::uint256_union const check_special;
    static rai::uint256_union const representative_special;
    static int const special_count;
    static unsigned const kdf_full_work = 256 * 1024;
    static unsigned const kdf_test_work = 8;
    static unsigned const kdf_work = rai::rai_network == rai::rai_networks::rai_test_network ? kdf_test_work : kdf_full_work;
	rai::mdb_env & environment;
    MDB_dbi handle;
};
class node;
// A wallet is a set of account keys encrypted by a common encryption key
class wallet : public std::enable_shared_from_this <rai::wallet>
{
public:
    wallet (bool &, rai::transaction &, rai::node &, std::string const &);
    wallet (bool &, rai::transaction &, rai::node &, std::string const &, std::string const &);
	void enter_initial_password ();
    bool valid_password ();
    bool enter_password (std::string const &);
	rai::public_key insert (rai::private_key const &);
    bool exists (rai::public_key const &);
	bool import (std::string const &, std::string const &);
	void serialize (std::string &);
	bool change_action (rai::account const &, rai::account const &);
    bool receive_action (rai::send_block const &, rai::private_key const &, rai::account const &, rai::uint128_union const &);
	rai::block_hash send_action (rai::account const &, rai::account const &, rai::uint128_t const &);
	bool change_sync (rai::account const &, rai::account const &);
    bool receive_sync (rai::send_block const &, rai::private_key const &, rai::account const &, rai::uint128_t const &);
	rai::block_hash send_sync (rai::account const &, rai::account const &, rai::uint128_t const &);
    void work_generate (rai::account const &, rai::block_hash const &);
    void work_update (MDB_txn *, rai::account const &, rai::block_hash const &, uint64_t);
    uint64_t work_fetch (MDB_txn *, rai::account const &, rai::block_hash const &);
	bool search_pending ();
	std::function <void (bool, bool)> lock_observer;
    rai::wallet_store store;
    rai::node & node;
};
// The wallets set is all the wallets a node controls.  A node may contain multiple wallets independently encrypted and operated.
class wallets
{
public:
	wallets (bool &, rai::node &);
	std::shared_ptr <rai::wallet> open (rai::uint256_union const &);
	std::shared_ptr <rai::wallet> create (rai::uint256_union const &);
    bool search_pending (rai::uint256_union const &);
	void destroy (rai::uint256_union const &);
	void queue_wallet_action (rai::account const &, rai::uint128_t const &, std::function <void ()> const &);
	void foreach_representative (std::function <void (rai::public_key const &, rai::private_key const &)> const &);
	std::function <void (rai::account const &, bool)> observer;
	std::unordered_map <rai::uint256_union, std::shared_ptr <rai::wallet>> items;
	std::unordered_map <rai::account, std::multimap <rai::uint128_t, std::function <void ()>, std::greater <rai::uint128_t>>> pending_actions;
	std::unordered_set <rai::account> current_actions;
	std::mutex action_mutex;
	MDB_dbi handle;
	rai::node & node;
};
}<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef NODE_HPP
#define NODE_HPP

#include <algorithm>
#include <map>
#include <set>

#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/msg.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/assert.hpp>

#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/ref.hpp>

#include "libtorrent/socket.hpp"

namespace libtorrent {

	namespace aux { struct session_impl; }
	struct session_status;

}

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(node);
#endif

struct traversal_algorithm;

// this is the entry for every peer
// the timestamp is there to make it possible
// to remove stale peers
struct peer_entry
{
	tcp::endpoint addr;
	ptime added;
};

// this is a group. It contains a set of group members
struct torrent_entry
{
	std::set<peer_entry> peers;
};

inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
{
	return lhs.addr.address() == rhs.addr.address()
		? lhs.addr.port() < rhs.addr.port()
		: lhs.addr.address() < rhs.addr.address();
}

struct null_type {};

class announce_observer : public observer
{
public:
	announce_observer(boost::pool<>& allocator
		, sha1_hash const& info_hash
		, int listen_port
		, entry const& write_token)
		: observer(allocator)
		, m_info_hash(info_hash)
		, m_listen_port(listen_port)
		, m_token(write_token)
	{}

	void send(msg& m)
	{
		m.port = m_listen_port;
		m.info_hash = m_info_hash;
		m.write_token = m_token;
	}

	void timeout() {}
	void reply(msg const&) {}
	void abort() {}

private:
	sha1_hash m_info_hash;
	int m_listen_port;
	entry m_token;
};

class get_peers_observer : public observer
{
public:
	get_peers_observer(sha1_hash const& info_hash
		, int listen_port
		, rpc_manager& rpc
		, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
		: observer(rpc.allocator())
		, m_info_hash(info_hash)
		, m_listen_port(listen_port)
		, m_rpc(rpc)
		, m_fun(f)
	{}

	void send(msg& m)
	{
		m.port = m_listen_port;
		m.info_hash = m_info_hash;
	}

	void timeout() {}
	void reply(msg const& r)
	{
		observer_ptr o(new (m_rpc.allocator().malloc()) announce_observer(
			m_rpc.allocator(), m_info_hash, m_listen_port, r.write_token));
#ifndef NDEBUG
		o->m_in_constructor = false;
#endif
		m_rpc.invoke(messages::announce_peer, r.addr, o);
		m_fun(r.peers, m_info_hash);
	}
	void abort() {}

private:
	sha1_hash m_info_hash;
	int m_listen_port;
	rpc_manager& m_rpc;
	boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
};

class node_impl : boost::noncopyable
{
typedef std::map<node_id, torrent_entry> table_t;
public:
	node_impl(libtorrent::aux::session_impl& ses, boost::function<void(msg const&)> const& f
		, dht_settings const& settings);

	virtual ~node_impl() {}

	void refresh(node_id const& id, boost::function0<void> f);
	void bootstrap(std::vector<udp::endpoint> const& nodes
		, boost::function0<void> f);
	void find_node(node_id const& id, boost::function<
	void(std::vector<node_entry> const&)> f);
	void add_router_node(udp::endpoint router);

	void unreachable(udp::endpoint const& ep);
	void incoming(msg const& m);

	void refresh();
	void refresh_bucket(int bucket);
	int bucket_size(int bucket);

	typedef routing_table::iterator iterator;

	iterator begin() const { return m_table.begin(); }
	iterator end() const { return m_table.end(); }

	typedef table_t::iterator data_iterator;

	void set_node_id(node_id const& nid) { m_id = nid; }
	node_id const& nid() const { return m_id; }

	boost::tuple<int, int> size() const{ return m_table.size(); }
	size_type num_global_nodes() const
	{ return m_table.num_global_nodes(); }

	data_iterator begin_data() { return m_map.begin(); }
	data_iterator end_data() { return m_map.end(); }
	int data_size() const { return int(m_map.size()); }

#ifdef TORRENT_DHT_VERBOSE_LOGGING
	void print_state(std::ostream& os) const
	{ m_table.print_state(os); }
#endif

	void announce(sha1_hash const& info_hash, int listen_port
		, boost::function<void(std::vector<tcp::endpoint> const&
			, sha1_hash const&)> f);

	bool verify_token(msg const& m);
	entry generate_token(msg const& m);

	// the returned time is the delay until connection_timeout()
	// should be called again the next time
	time_duration connection_timeout();
	time_duration refresh_timeout();

	// generates a new secret number used to generate write tokens
	void new_write_key();

	// pings the given node, and adds it to
	// the routing table if it respons and if the
	// bucket is not full.
	void add_node(udp::endpoint node);

	void replacement_cache(bucket_t& nodes) const
	{ m_table.replacement_cache(nodes); }

	int branch_factor() const { return m_settings.search_branching; }

	void add_traversal_algorithm(traversal_algorithm* a)
	{ m_running_requests.insert(a); }

	void remove_traversal_algorithm(traversal_algorithm* a)
	{ m_running_requests.erase(a); }

	void status(libtorrent::session_status& s);

protected:
	// is called when a find data request is received. Should
	// return false if the data is not stored on this node. If
	// the data is stored, it should be serialized into 'data'.
	bool on_find(msg const& m, std::vector<tcp::endpoint>& peers) const;

	// this is called when a store request is received. The data
	// is store-parameters and the data to be stored.
	void on_announce(msg const& m, msg& reply);

	dht_settings const& m_settings;

	// the maximum number of peers to send in a get_peers
	// reply. Ordinary trackers usually limit this to 50.
	// 50 => 6 * 50 = 250 bytes + packet overhead
	int m_max_peers_reply;

private:
	// this list must be destructed after the rpc manager
	// since it might have references to it
	std::set<traversal_algorithm*> m_running_requests;

	void incoming_request(msg const& h);

	node_id m_id;

public:
	routing_table m_table;
	rpc_manager m_rpc;

private:
	table_t m_map;

	ptime m_last_tracker_tick;

	// secret random numbers used to create write tokens
	int m_secret[2];

	libtorrent::aux::session_impl& m_ses;
};

} } // namespace libtorrent::dht

#endif // NODE_HPP

<commit_msg>mutex fix for dht status reporting<commit_after>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef NODE_HPP
#define NODE_HPP

#include <algorithm>
#include <map>
#include <set>

#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/msg.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/assert.hpp>

#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/ref.hpp>
#include <boost/thread/mutex.hpp>

#include "libtorrent/socket.hpp"

namespace libtorrent {

	namespace aux { struct session_impl; }
	struct session_status;

}

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(node);
#endif

struct traversal_algorithm;

// this is the entry for every peer
// the timestamp is there to make it possible
// to remove stale peers
struct peer_entry
{
	tcp::endpoint addr;
	ptime added;
};

// this is a group. It contains a set of group members
struct torrent_entry
{
	std::set<peer_entry> peers;
};

inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
{
	return lhs.addr.address() == rhs.addr.address()
		? lhs.addr.port() < rhs.addr.port()
		: lhs.addr.address() < rhs.addr.address();
}

struct null_type {};

class announce_observer : public observer
{
public:
	announce_observer(boost::pool<>& allocator
		, sha1_hash const& info_hash
		, int listen_port
		, entry const& write_token)
		: observer(allocator)
		, m_info_hash(info_hash)
		, m_listen_port(listen_port)
		, m_token(write_token)
	{}

	void send(msg& m)
	{
		m.port = m_listen_port;
		m.info_hash = m_info_hash;
		m.write_token = m_token;
	}

	void timeout() {}
	void reply(msg const&) {}
	void abort() {}

private:
	sha1_hash m_info_hash;
	int m_listen_port;
	entry m_token;
};

class get_peers_observer : public observer
{
public:
	get_peers_observer(sha1_hash const& info_hash
		, int listen_port
		, rpc_manager& rpc
		, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
		: observer(rpc.allocator())
		, m_info_hash(info_hash)
		, m_listen_port(listen_port)
		, m_rpc(rpc)
		, m_fun(f)
	{}

	void send(msg& m)
	{
		m.port = m_listen_port;
		m.info_hash = m_info_hash;
	}

	void timeout() {}
	void reply(msg const& r)
	{
		observer_ptr o(new (m_rpc.allocator().malloc()) announce_observer(
			m_rpc.allocator(), m_info_hash, m_listen_port, r.write_token));
#ifndef NDEBUG
		o->m_in_constructor = false;
#endif
		m_rpc.invoke(messages::announce_peer, r.addr, o);
		m_fun(r.peers, m_info_hash);
	}
	void abort() {}

private:
	sha1_hash m_info_hash;
	int m_listen_port;
	rpc_manager& m_rpc;
	boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
};

class node_impl : boost::noncopyable
{
typedef std::map<node_id, torrent_entry> table_t;
public:
	node_impl(libtorrent::aux::session_impl& ses, boost::function<void(msg const&)> const& f
		, dht_settings const& settings);

	virtual ~node_impl() {}

	void refresh(node_id const& id, boost::function0<void> f);
	void bootstrap(std::vector<udp::endpoint> const& nodes
		, boost::function0<void> f);
	void find_node(node_id const& id, boost::function<
	void(std::vector<node_entry> const&)> f);
	void add_router_node(udp::endpoint router);

	void unreachable(udp::endpoint const& ep);
	void incoming(msg const& m);

	void refresh();
	void refresh_bucket(int bucket);
	int bucket_size(int bucket);

	typedef routing_table::iterator iterator;

	iterator begin() const { return m_table.begin(); }
	iterator end() const { return m_table.end(); }

	typedef table_t::iterator data_iterator;

	void set_node_id(node_id const& nid) { m_id = nid; }
	node_id const& nid() const { return m_id; }

	boost::tuple<int, int> size() const{ return m_table.size(); }
	size_type num_global_nodes() const
	{ return m_table.num_global_nodes(); }

	data_iterator begin_data() { return m_map.begin(); }
	data_iterator end_data() { return m_map.end(); }
	int data_size() const { return int(m_map.size()); }

#ifdef TORRENT_DHT_VERBOSE_LOGGING
	void print_state(std::ostream& os) const
	{ m_table.print_state(os); }
#endif

	void announce(sha1_hash const& info_hash, int listen_port
		, boost::function<void(std::vector<tcp::endpoint> const&
			, sha1_hash const&)> f);

	bool verify_token(msg const& m);
	entry generate_token(msg const& m);

	// the returned time is the delay until connection_timeout()
	// should be called again the next time
	time_duration connection_timeout();
	time_duration refresh_timeout();

	// generates a new secret number used to generate write tokens
	void new_write_key();

	// pings the given node, and adds it to
	// the routing table if it respons and if the
	// bucket is not full.
	void add_node(udp::endpoint node);

	void replacement_cache(bucket_t& nodes) const
	{ m_table.replacement_cache(nodes); }

	int branch_factor() const { return m_settings.search_branching; }

	void add_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.insert(a);
	}

	void remove_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.erase(a);
	}

	void status(libtorrent::session_status& s);

protected:
	// is called when a find data request is received. Should
	// return false if the data is not stored on this node. If
	// the data is stored, it should be serialized into 'data'.
	bool on_find(msg const& m, std::vector<tcp::endpoint>& peers) const;

	// this is called when a store request is received. The data
	// is store-parameters and the data to be stored.
	void on_announce(msg const& m, msg& reply);

	dht_settings const& m_settings;

	// the maximum number of peers to send in a get_peers
	// reply. Ordinary trackers usually limit this to 50.
	// 50 => 6 * 50 = 250 bytes + packet overhead
	int m_max_peers_reply;

private:
	typedef boost::mutex mutex_t;
	mutex_t m_mutex;

	// this list must be destructed after the rpc manager
	// since it might have references to it
	std::set<traversal_algorithm*> m_running_requests;

	void incoming_request(msg const& h);

	node_id m_id;

public:
	routing_table m_table;
	rpc_manager m_rpc;

private:
	table_t m_map;

	ptime m_last_tracker_tick;

	// secret random numbers used to create write tokens
	int m_secret[2];

	libtorrent::aux::session_impl& m_ses;
};

} } // namespace libtorrent::dht

#endif // NODE_HPP

<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef NODE_HPP
#define NODE_HPP

#include <algorithm>
#include <map>
#include <set>

#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/msg.hpp>
#include <libtorrent/kademlia/find_data.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/assert.hpp>
#include <libtorrent/thread.hpp>

#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>

#include "libtorrent/socket.hpp"

namespace libtorrent {

	namespace aux { struct session_impl; }
	struct session_status;

}

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(node);
#endif

class traversal_algorithm;

struct key_desc_t
{
	char const* name;
	int type;
	int size;
	int flags;

	enum { optional = 1};
};

bool TORRENT_EXPORT verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry const* ret[]
	, int size , char* error, int error_size);

// this is the entry for every peer
// the timestamp is there to make it possible
// to remove stale peers
struct peer_entry
{
	tcp::endpoint addr;
	ptime added;
};

// this is a group. It contains a set of group members
struct torrent_entry
{
	std::set<peer_entry> peers;
};

// this is the entry for a torrent that has been published
// in the DHT.
struct search_torrent_entry
{
	// the tags of the torrent. The key of
	// this entry is the sha-1 hash of one of
	// these tags. The counter is the number of
	// times a tag has been included in a publish
	// call. The counters are periodically
	// decremented by a factor, so that the
	// popularity ratio between the tags is
	// maintained. The decrement is rounded down.
	std::map<std::string, int> tags;

	// this is the sum of all values in the tags
	// map. It is only an optimization to avoid
	// recalculating it constantly
	int total_tag_points;

	// the name of the torrent
	std::map<std::string, int> name;
	int total_name_points;

	// increase the popularity counters for this torrent
	void publish(std::string const& name, char const* in_tags[], int num_tags);

	// return a score of how well this torrent matches
	// the given set of tags. Each word in the string
	// (separated by a space) is considered a tag.
	// tags with 2 letters or fewer are ignored
	int match(char const* tags[], int num_tags) const;

	// this is called once every hour, and will
	// decrement the popularity counters of the
	// tags. Returns true if this entry should
	// be deleted
	bool tick();

	void get_name(std::string& t) const;
	void get_tags(std::string& t) const;
};

inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
{
	return lhs.addr.address() == rhs.addr.address()
		? lhs.addr.port() < rhs.addr.port()
		: lhs.addr.address() < rhs.addr.address();
}

struct null_type {};

class announce_observer : public observer
{
public:
	announce_observer(boost::intrusive_ptr<traversal_algorithm> const& algo)
		: observer(algo)
	{}

	void reply(msg const&) { m_done = true; }
};

struct count_peers
{
	int& count;
	count_peers(int& c): count(c) {}
	void operator()(std::pair<libtorrent::dht::node_id
		, libtorrent::dht::torrent_entry> const& t)
	{
		count += t.second.peers.size();
	}
};

class node_impl : boost::noncopyable
{
typedef std::map<node_id, torrent_entry> table_t;
typedef std::map<std::pair<node_id, sha1_hash>, search_torrent_entry> search_table_t;
public:
	node_impl(libtorrent::aux::session_impl& ses
		, bool (*f)(void*, entry const&, udp::endpoint const&, int)
		, dht_settings const& settings, boost::optional<node_id> nid
		, void* userdata);

	virtual ~node_impl() {}

	void refresh(node_id const& id, find_data::nodes_callback const& f);
	void bootstrap(std::vector<udp::endpoint> const& nodes
		, find_data::nodes_callback const& f);
	void add_router_node(udp::endpoint router);

	void unreachable(udp::endpoint const& ep);
	void incoming(msg const& m);

	int num_torrents() const { return m_map.size(); }
	int num_peers() const
	{
		int ret = 0;
		std::for_each(m_map.begin(), m_map.end(), count_peers(ret));
		return ret;
	}

	void refresh();
	void refresh_bucket(int bucket);
	int bucket_size(int bucket);

	typedef routing_table::iterator iterator;

	iterator begin() const { return m_table.begin(); }
	iterator end() const { return m_table.end(); }

	node_id const& nid() const { return m_id; }

	boost::tuple<int, int> size() const{ return m_table.size(); }
	size_type num_global_nodes() const
	{ return m_table.num_global_nodes(); }

	int data_size() const { return int(m_map.size()); }

#ifdef TORRENT_DHT_VERBOSE_LOGGING
	void print_state(std::ostream& os) const
	{ m_table.print_state(os); }
#endif

	void announce(sha1_hash const& info_hash, int listen_port
		, boost::function<void(std::vector<tcp::endpoint> const&)> f);

	bool verify_token(std::string const& token, char const* info_hash
		, udp::endpoint const& addr);

	std::string generate_token(udp::endpoint const& addr, char const* info_hash);

	// the returned time is the delay until connection_timeout()
	// should be called again the next time
	time_duration connection_timeout();
	time_duration refresh_timeout();

	// generates a new secret number used to generate write tokens
	void new_write_key();

	// pings the given node, and adds it to
	// the routing table if it respons and if the
	// bucket is not full.
	void add_node(udp::endpoint node);

	void replacement_cache(bucket_t& nodes) const
	{ m_table.replacement_cache(nodes); }

	int branch_factor() const { return m_settings.search_branching; }

	void add_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.insert(a);
	}

	void remove_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.erase(a);
	}

	void status(libtorrent::session_status& s);

protected:
	// is called when a find data request is received. Should
	// return false if the data is not stored on this node. If
	// the data is stored, it should be serialized into 'data'.
	bool lookup_peers(sha1_hash const& info_hash, entry& reply) const;
	bool lookup_torrents(sha1_hash const& target, entry& reply
		, char* tags) const;

	dht_settings const& m_settings;

	// the maximum number of peers to send in a get_peers
	// reply. Ordinary trackers usually limit this to 50.
	// 50 => 6 * 50 = 250 bytes + packet overhead
	int m_max_peers_reply;

private:
	typedef libtorrent::mutex mutex_t;
	mutex_t m_mutex;

	// this list must be destructed after the rpc manager
	// since it might have references to it
	std::set<traversal_algorithm*> m_running_requests;

	void incoming_request(msg const& h, entry& e);

	node_id m_id;

public:
	routing_table m_table;
	rpc_manager m_rpc;

private:
	table_t m_map;
	search_table_t m_search_map;

	ptime m_last_tracker_tick;

	// secret random numbers used to create write tokens
	int m_secret[2];

	libtorrent::aux::session_impl& m_ses;
	bool (*m_send)(void*, entry const&, udp::endpoint const&, int);
	void* m_userdata;
};

} } // namespace libtorrent::dht

#endif // NODE_HPP

<commit_msg>dht fix<commit_after>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef NODE_HPP
#define NODE_HPP

#include <algorithm>
#include <map>
#include <set>

#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/msg.hpp>
#include <libtorrent/kademlia/find_data.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
#include <libtorrent/assert.hpp>
#include <libtorrent/thread.hpp>

#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>

#include "libtorrent/socket.hpp"

namespace libtorrent {

	namespace aux { struct session_impl; }
	struct session_status;

}

namespace libtorrent { namespace dht
{

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(node);
#endif

class traversal_algorithm;

struct key_desc_t
{
	char const* name;
	int type;
	int size;
	int flags;

	enum { optional = 1};
};

bool TORRENT_EXPORT verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry const* ret[]
	, int size , char* error, int error_size);

// this is the entry for every peer
// the timestamp is there to make it possible
// to remove stale peers
struct peer_entry
{
	tcp::endpoint addr;
	ptime added;
};

// this is a group. It contains a set of group members
struct torrent_entry
{
	std::set<peer_entry> peers;
};

// this is the entry for a torrent that has been published
// in the DHT.
struct search_torrent_entry
{
	search_torrent_entry(): total_tag_points(0), total_name_points(0) {}

	// the tags of the torrent. The key of
	// this entry is the sha-1 hash of one of
	// these tags. The counter is the number of
	// times a tag has been included in a publish
	// call. The counters are periodically
	// decremented by a factor, so that the
	// popularity ratio between the tags is
	// maintained. The decrement is rounded down.
	std::map<std::string, int> tags;

	// this is the sum of all values in the tags
	// map. It is only an optimization to avoid
	// recalculating it constantly
	int total_tag_points;

	// the name of the torrent
	std::map<std::string, int> name;
	int total_name_points;

	// increase the popularity counters for this torrent
	void publish(std::string const& name, char const* in_tags[], int num_tags);

	// return a score of how well this torrent matches
	// the given set of tags. Each word in the string
	// (separated by a space) is considered a tag.
	// tags with 2 letters or fewer are ignored
	int match(char const* tags[], int num_tags) const;

	// this is called once every hour, and will
	// decrement the popularity counters of the
	// tags. Returns true if this entry should
	// be deleted
	bool tick();

	void get_name(std::string& t) const;
	void get_tags(std::string& t) const;
};

inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
{
	return lhs.addr.address() == rhs.addr.address()
		? lhs.addr.port() < rhs.addr.port()
		: lhs.addr.address() < rhs.addr.address();
}

struct null_type {};

class announce_observer : public observer
{
public:
	announce_observer(boost::intrusive_ptr<traversal_algorithm> const& algo)
		: observer(algo)
	{}

	void reply(msg const&) { m_done = true; }
};

struct count_peers
{
	int& count;
	count_peers(int& c): count(c) {}
	void operator()(std::pair<libtorrent::dht::node_id
		, libtorrent::dht::torrent_entry> const& t)
	{
		count += t.second.peers.size();
	}
};

class node_impl : boost::noncopyable
{
typedef std::map<node_id, torrent_entry> table_t;
typedef std::map<std::pair<node_id, sha1_hash>, search_torrent_entry> search_table_t;
public:
	node_impl(libtorrent::aux::session_impl& ses
		, bool (*f)(void*, entry const&, udp::endpoint const&, int)
		, dht_settings const& settings, boost::optional<node_id> nid
		, void* userdata);

	virtual ~node_impl() {}

	void refresh(node_id const& id, find_data::nodes_callback const& f);
	void bootstrap(std::vector<udp::endpoint> const& nodes
		, find_data::nodes_callback const& f);
	void add_router_node(udp::endpoint router);

	void unreachable(udp::endpoint const& ep);
	void incoming(msg const& m);

	int num_torrents() const { return m_map.size(); }
	int num_peers() const
	{
		int ret = 0;
		std::for_each(m_map.begin(), m_map.end(), count_peers(ret));
		return ret;
	}

	void refresh();
	void refresh_bucket(int bucket);
	int bucket_size(int bucket);

	typedef routing_table::iterator iterator;

	iterator begin() const { return m_table.begin(); }
	iterator end() const { return m_table.end(); }

	node_id const& nid() const { return m_id; }

	boost::tuple<int, int> size() const{ return m_table.size(); }
	size_type num_global_nodes() const
	{ return m_table.num_global_nodes(); }

	int data_size() const { return int(m_map.size()); }

#ifdef TORRENT_DHT_VERBOSE_LOGGING
	void print_state(std::ostream& os) const
	{ m_table.print_state(os); }
#endif

	void announce(sha1_hash const& info_hash, int listen_port
		, boost::function<void(std::vector<tcp::endpoint> const&)> f);

	bool verify_token(std::string const& token, char const* info_hash
		, udp::endpoint const& addr);

	std::string generate_token(udp::endpoint const& addr, char const* info_hash);

	// the returned time is the delay until connection_timeout()
	// should be called again the next time
	time_duration connection_timeout();
	time_duration refresh_timeout();

	// generates a new secret number used to generate write tokens
	void new_write_key();

	// pings the given node, and adds it to
	// the routing table if it respons and if the
	// bucket is not full.
	void add_node(udp::endpoint node);

	void replacement_cache(bucket_t& nodes) const
	{ m_table.replacement_cache(nodes); }

	int branch_factor() const { return m_settings.search_branching; }

	void add_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.insert(a);
	}

	void remove_traversal_algorithm(traversal_algorithm* a)
	{
		mutex_t::scoped_lock l(m_mutex);
		m_running_requests.erase(a);
	}

	void status(libtorrent::session_status& s);

protected:
	// is called when a find data request is received. Should
	// return false if the data is not stored on this node. If
	// the data is stored, it should be serialized into 'data'.
	bool lookup_peers(sha1_hash const& info_hash, entry& reply) const;
	bool lookup_torrents(sha1_hash const& target, entry& reply
		, char* tags) const;

	dht_settings const& m_settings;

	// the maximum number of peers to send in a get_peers
	// reply. Ordinary trackers usually limit this to 50.
	// 50 => 6 * 50 = 250 bytes + packet overhead
	int m_max_peers_reply;

private:
	typedef libtorrent::mutex mutex_t;
	mutex_t m_mutex;

	// this list must be destructed after the rpc manager
	// since it might have references to it
	std::set<traversal_algorithm*> m_running_requests;

	void incoming_request(msg const& h, entry& e);

	node_id m_id;

public:
	routing_table m_table;
	rpc_manager m_rpc;

private:
	table_t m_map;
	search_table_t m_search_map;

	ptime m_last_tracker_tick;

	// secret random numbers used to create write tokens
	int m_secret[2];

	libtorrent::aux::session_impl& m_ses;
	bool (*m_send)(void*, entry const&, udp::endpoint const&, int);
	void* m_userdata;
};

} } // namespace libtorrent::dht

#endif // NODE_HPP

<|endoftext|>