librats 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,9 +48,10 @@ struct RatsPeer {
48
48
  // Handshake-related fields
49
49
  enum class HandshakeState {
50
50
  PENDING, // Handshake not started
51
- SENT, // Handshake sent, waiting for response
52
- COMPLETED, // Handshake completed successfully
53
- FAILED // Handshake failed
51
+ SENT, // Handshake sent, waiting for response
52
+ NOISE_PENDING, // Rats handshake done, Noise handshake in progress
53
+ COMPLETED, // Handshake completed successfully (including Noise if enabled)
54
+ FAILED // Handshake failed
54
55
  };
55
56
 
56
57
  HandshakeState handshake_state; // Current handshake state
@@ -635,6 +636,13 @@ public:
635
636
  */
636
637
  bool is_automatic_discovery_running() const;
637
638
 
639
+ /**
640
+ * Calculate discovery interval based on current peer count
641
+ * Uses graduated scaling: more aggressive when fewer peers, less aggressive when nearly full
642
+ * @return Discovery interval in seconds
643
+ */
644
+ std::chrono::seconds calculate_discovery_interval() const;
645
+
638
646
  /**
639
647
  * Get the discovery hash for current protocol configuration
640
648
  * @return Discovery hash based on current protocol name and version
@@ -990,6 +998,20 @@ public:
990
998
  // Logging Control API
991
999
  // =========================================================================
992
1000
 
1001
+ /**
1002
+ * Enable or disable console logging
1003
+ * When disabled, log messages will not be printed to stdout/stderr
1004
+ * File logging (if enabled) will still work
1005
+ * @param enabled Whether to enable console logging (default: true)
1006
+ */
1007
+ void set_console_logging_enabled(bool enabled);
1008
+
1009
+ /**
1010
+ * Check if console logging is currently enabled
1011
+ * @return true if console logging is enabled
1012
+ */
1013
+ bool is_console_logging_enabled() const;
1014
+
993
1015
  /**
994
1016
  * Enable or disable file logging
995
1017
  * When enabled, logs will be written to "rats.log" by default
@@ -1069,6 +1091,20 @@ public:
1069
1091
  */
1070
1092
  void set_log_retention_count(int count);
1071
1093
 
1094
+ /**
1095
+ * Enable or disable log rotation on application startup
1096
+ * When enabled, the existing log file will be rotated when logging starts,
1097
+ * so each application run gets a fresh log file
1098
+ * @param enabled Whether to rotate logs on startup (default: false)
1099
+ */
1100
+ void set_log_rotate_on_startup(bool enabled);
1101
+
1102
+ /**
1103
+ * Check if log rotation on startup is enabled
1104
+ * @return true if rotate on startup is enabled
1105
+ */
1106
+ bool is_log_rotate_on_startup_enabled() const;
1107
+
1072
1108
  /**
1073
1109
  * Clear/reset the current log file
1074
1110
  */
@@ -2046,15 +2082,16 @@ private:
2046
2082
  std::string message_type;
2047
2083
  int64_t timestamp;
2048
2084
  bool encryption_enabled; // Whether peer supports/wants encryption
2085
+ uint16_t listen_port; // Peer's listening port for peer exchange
2049
2086
  };
2050
2087
 
2051
2088
  std::string create_handshake_message(const std::string& message_type, const std::string& our_peer_id) const;
2052
- bool parse_handshake_message(const std::string& message, HandshakeMessage& out_msg) const;
2089
+ bool parse_handshake_message(const std::vector<uint8_t>& data, HandshakeMessage& out_msg) const;
2053
2090
  bool validate_handshake_message(const HandshakeMessage& msg) const;
2054
- bool is_handshake_message(const std::string& message) const;
2091
+ bool is_handshake_message(const std::vector<uint8_t>& data) const;
2055
2092
  bool send_handshake(socket_t socket, const std::string& our_peer_id);
2056
2093
  bool send_handshake_unlocked(socket_t socket, const std::string& our_peer_id);
2057
- bool handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::string& message);
2094
+ bool handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::vector<uint8_t>& data);
2058
2095
  void check_handshake_timeouts();
2059
2096
  void log_handshake_completion_unlocked(const RatsPeer& peer);
2060
2097
 
@@ -248,12 +248,13 @@ void RatsClient::get_torrent_metadata_from_peer(const InfoHash& info_hash,
248
248
  return;
249
249
  }
250
250
 
251
- LOG_CLIENT_INFO("Fetching metadata from peer " << peer_ip << ":" << peer_port << " (fast path)");
251
+ LOG_CLIENT_INFO("Fetching metadata from peer " << peer_ip << ":" << peer_port << " (fast path, no DHT)");
252
252
 
253
253
  // Add magnet in metadata-only mode (empty save_path) and immediately add the peer
254
+ // skip_dht_search=true to avoid network-wide DHT queries - we only want this specific peer
254
255
  std::string hash_hex = info_hash_to_hex(info_hash);
255
256
  std::string magnet = "magnet:?xt=urn:btih:" + hash_hex;
256
- auto torrent = bittorrent_client_->add_magnet(magnet, "");
257
+ auto torrent = bittorrent_client_->add_magnet(magnet, "", true /* skip_dht_search */);
257
258
 
258
259
  if (!torrent) {
259
260
  if (callback) {
@@ -161,6 +161,14 @@ char* rats_get_connection_statistics_json(rats_client_t handle) {
161
161
  return rats_strdup_owned(json.dump());
162
162
  }
163
163
 
164
+ void rats_set_console_logging_enabled(int enabled) {
165
+ Logger::getInstance().set_console_logging_enabled(enabled != 0);
166
+ }
167
+
168
+ int rats_is_console_logging_enabled(void) {
169
+ return Logger::getInstance().is_console_logging_enabled() ? 1 : 0;
170
+ }
171
+
164
172
  void rats_set_logging_enabled(int enabled) {
165
173
  // Global logger control through any client instance is awkward; use singleton
166
174
  Logger::getInstance().set_file_logging_enabled(enabled != 0);
@@ -229,6 +237,18 @@ void rats_set_log_retention_count(rats_client_t handle, int count) {
229
237
  wrap->client->set_log_retention_count(count);
230
238
  }
231
239
 
240
+ void rats_set_log_rotate_on_startup(rats_client_t handle, int enabled) {
241
+ if (!handle) return;
242
+ rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
243
+ wrap->client->set_log_rotate_on_startup(enabled != 0);
244
+ }
245
+
246
+ int rats_is_log_rotate_on_startup_enabled(rats_client_t handle) {
247
+ if (!handle) return 0;
248
+ rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
249
+ return wrap->client->is_log_rotate_on_startup_enabled() ? 1 : 0;
250
+ }
251
+
232
252
  void rats_clear_log_file(rats_client_t handle) {
233
253
  if (!handle) return;
234
254
  rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
@@ -41,6 +41,8 @@ RATS_API char** rats_get_peer_ids(rats_client_t client, int* count); // caller m
41
41
  RATS_API char* rats_get_peer_info_json(rats_client_t client, const char* peer_id); // caller must free
42
42
 
43
43
  // Logging controls (optional helpers)
44
+ RATS_API void rats_set_console_logging_enabled(int enabled);
45
+ RATS_API int rats_is_console_logging_enabled(void);
44
46
  RATS_API void rats_set_logging_enabled(int enabled);
45
47
  RATS_API void rats_set_log_level(const char* level_str); // "DEBUG", "INFO", "WARN", "ERROR"
46
48
  RATS_API void rats_set_log_file_path(rats_client_t client, const char* file_path);
@@ -51,6 +53,8 @@ RATS_API void rats_set_log_timestamps_enabled(rats_client_t client, int enabled)
51
53
  RATS_API int rats_is_log_timestamps_enabled(rats_client_t client);
52
54
  RATS_API void rats_set_log_rotation_size(rats_client_t client, size_t max_size_bytes);
53
55
  RATS_API void rats_set_log_retention_count(rats_client_t client, int count);
56
+ RATS_API void rats_set_log_rotate_on_startup(rats_client_t client, int enabled);
57
+ RATS_API int rats_is_log_rotate_on_startup_enabled(rats_client_t client);
54
58
  RATS_API void rats_clear_log_file(rats_client_t client);
55
59
 
56
60
  // Error codes
@@ -103,6 +103,11 @@ std::vector<uint8_t> RatsClient::get_peer_handshake_hash(const std::string& peer
103
103
  }
104
104
 
105
105
  bool RatsClient::send_noise_message(socket_t socket, const uint8_t* data, size_t len) {
106
+ // Get socket-specific mutex for thread-safe sending
107
+ // This prevents race conditions with other threads sending rats protocol messages
108
+ auto socket_mutex = get_socket_send_mutex(socket);
109
+ std::lock_guard<std::mutex> send_lock(*socket_mutex);
110
+
106
111
  // Send length-prefixed message for Noise handshake
107
112
  uint32_t network_len = htonl(static_cast<uint32_t>(len));
108
113
 
@@ -14,6 +14,18 @@ namespace librats {
14
14
  // Logging Control API Implementation
15
15
  //=============================================================================
16
16
 
17
+ void RatsClient::set_console_logging_enabled(bool enabled) {
18
+ Logger::getInstance().set_console_logging_enabled(enabled);
19
+ // Only log if console is enabled, otherwise we're silently disabling
20
+ if (enabled) {
21
+ LOG_CLIENT_INFO("Console logging enabled");
22
+ }
23
+ }
24
+
25
+ bool RatsClient::is_console_logging_enabled() const {
26
+ return Logger::getInstance().is_console_logging_enabled();
27
+ }
28
+
17
29
  void RatsClient::set_logging_enabled(bool enabled) {
18
30
  LOG_CLIENT_INFO("Setting file logging " << (enabled ? "enabled" : "disabled"));
19
31
 
@@ -124,6 +136,15 @@ void RatsClient::set_log_retention_count(int count) {
124
136
  LOG_CLIENT_INFO("Log retention count set to: " << count << " files");
125
137
  }
126
138
 
139
+ void RatsClient::set_log_rotate_on_startup(bool enabled) {
140
+ Logger::getInstance().set_rotate_on_startup(enabled);
141
+ LOG_CLIENT_INFO("Log rotation on startup " << (enabled ? "enabled" : "disabled"));
142
+ }
143
+
144
+ bool RatsClient::is_log_rotate_on_startup_enabled() const {
145
+ return Logger::getInstance().is_rotate_on_startup_enabled();
146
+ }
147
+
127
148
  void RatsClient::clear_log_file() {
128
149
  Logger& logger = Logger::getInstance();
129
150
  std::string log_path = logger.get_log_file_path();
@@ -59,6 +59,17 @@ public:
59
59
  timestamps_enabled_ = enabled;
60
60
  }
61
61
 
62
+ // Console logging configuration
63
+ void set_console_logging_enabled(bool enabled) {
64
+ std::lock_guard<std::mutex> lock(mutex_);
65
+ console_logging_enabled_ = enabled;
66
+ }
67
+
68
+ bool is_console_logging_enabled() const {
69
+ std::lock_guard<std::mutex> lock(mutex_);
70
+ return console_logging_enabled_;
71
+ }
72
+
62
73
  // File logging configuration
63
74
  void set_file_logging_enabled(bool enabled) {
64
75
  std::lock_guard<std::mutex> lock(mutex_);
@@ -89,6 +100,17 @@ public:
89
100
  max_log_files_ = count;
90
101
  }
91
102
 
103
+ // Rotate on startup configuration
104
+ void set_rotate_on_startup(bool enabled) {
105
+ std::lock_guard<std::mutex> lock(mutex_);
106
+ rotate_on_startup_ = enabled;
107
+ }
108
+
109
+ bool is_rotate_on_startup_enabled() const {
110
+ std::lock_guard<std::mutex> lock(mutex_);
111
+ return rotate_on_startup_;
112
+ }
113
+
92
114
  // Get current file logging status
93
115
  bool is_file_logging_enabled() const {
94
116
  std::lock_guard<std::mutex> lock(mutex_);
@@ -147,13 +169,15 @@ public:
147
169
  // Add message
148
170
  console_oss << " " << message << std::endl;
149
171
 
150
- // Output to appropriate console stream
151
- if (level >= LogLevel::ERROR) {
152
- std::cerr << console_oss.str();
153
- std::cerr.flush();
154
- } else {
155
- std::cout << console_oss.str();
156
- std::cout.flush();
172
+ // Output to appropriate console stream (if console logging is enabled)
173
+ if (console_logging_enabled_) {
174
+ if (level >= LogLevel::ERROR) {
175
+ std::cerr << console_oss.str();
176
+ std::cerr.flush();
177
+ } else {
178
+ std::cout << console_oss.str();
179
+ std::cout.flush();
180
+ }
157
181
  }
158
182
 
159
183
  // Also write to file if file logging is enabled
@@ -164,8 +188,9 @@ public:
164
188
 
165
189
  private:
166
190
  Logger() : min_level_(LogLevel::INFO), colors_enabled_(true), timestamps_enabled_(true),
167
- file_logging_enabled_(false), max_log_file_size_(10 * 1024 * 1024),
168
- max_log_files_(5), current_file_size_(0) {
191
+ console_logging_enabled_(true), file_logging_enabled_(false),
192
+ max_log_file_size_(10 * 1024 * 1024), max_log_files_(5), current_file_size_(0),
193
+ rotate_on_startup_(false), startup_rotation_done_(false) {
169
194
  // Check if we're outputting to a terminal
170
195
  is_terminal_ = isatty(fileno(stdout));
171
196
 
@@ -317,6 +342,19 @@ private:
317
342
  }
318
343
  }
319
344
 
345
+ // Perform rotation on startup if enabled and not yet done this session
346
+ if (rotate_on_startup_ && !startup_rotation_done_) {
347
+ // Check if the log file exists and has content
348
+ if (file_exists(log_file_path_.c_str())) {
349
+ std::ifstream check_file(log_file_path_, std::ios::ate);
350
+ if (check_file.is_open() && check_file.tellg() > 0) {
351
+ check_file.close();
352
+ rotate_log_files_impl();
353
+ }
354
+ }
355
+ startup_rotation_done_ = true;
356
+ }
357
+
320
358
  log_file_.open(log_file_path_, std::ios::app);
321
359
  if (log_file_.is_open()) {
322
360
  // Get current file size
@@ -337,6 +375,16 @@ private:
337
375
  if (log_file_path_.empty() || max_log_files_ <= 0) return;
338
376
 
339
377
  close_log_file();
378
+ rotate_log_files_impl();
379
+
380
+ // Reopen the log file (new empty file)
381
+ open_log_file();
382
+ }
383
+
384
+ // Internal rotation implementation - does not close/open files
385
+ // Called directly when we need to rotate before opening (e.g., on startup)
386
+ void rotate_log_files_impl() {
387
+ if (log_file_path_.empty() || max_log_files_ <= 0) return;
340
388
 
341
389
  // Move existing log files
342
390
  for (int i = max_log_files_ - 1; i >= 1; i--) {
@@ -355,9 +403,6 @@ private:
355
403
  // Move current log file to .1
356
404
  std::string backup_name = log_file_path_ + ".1";
357
405
  std::rename(log_file_path_.c_str(), backup_name.c_str());
358
-
359
- // Reopen the log file (new empty file)
360
- open_log_file();
361
406
  }
362
407
 
363
408
  mutable std::mutex mutex_;
@@ -366,6 +411,9 @@ private:
366
411
  bool timestamps_enabled_;
367
412
  bool is_terminal_;
368
413
 
414
+ // Console logging control
415
+ bool console_logging_enabled_;
416
+
369
417
  // File logging members
370
418
  bool file_logging_enabled_;
371
419
  std::string log_file_path_;
@@ -373,6 +421,10 @@ private:
373
421
  size_t max_log_file_size_;
374
422
  int max_log_files_;
375
423
  size_t current_file_size_;
424
+
425
+ // Rotate on startup members
426
+ bool rotate_on_startup_;
427
+ bool startup_rotation_done_; // Tracks if we've already done startup rotation for this session
376
428
  };
377
429
 
378
430
  } // namespace librats