librats 0.7.0 → 0.7.1
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.
- package/lib/index.d.ts +15 -0
- package/native-src/src/librats.cpp +113 -63
- package/native-src/src/librats.h +22 -6
- package/native-src/src/librats_c.cpp +8 -0
- package/native-src/src/librats_c.h +2 -0
- package/native-src/src/librats_encryption.cpp +5 -0
- package/native-src/src/librats_logging.cpp +12 -0
- package/native-src/src/logger.h +25 -9
- package/package.json +1 -1
- package/src/librats_node.cpp +23 -0
package/lib/index.d.ts
CHANGED
|
@@ -696,3 +696,18 @@ export function getGitDescribe(): string;
|
|
|
696
696
|
*/
|
|
697
697
|
export function getAbi(): number;
|
|
698
698
|
|
|
699
|
+
// ============ Logging Control Functions ============
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Enable or disable console logging.
|
|
703
|
+
* When disabled, log messages will not be printed to stdout/stderr.
|
|
704
|
+
* File logging (if enabled) will still work.
|
|
705
|
+
* @param enabled - Whether to enable console logging (default: true)
|
|
706
|
+
*/
|
|
707
|
+
export function setConsoleLoggingEnabled(enabled: boolean): void;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Check if console logging is currently enabled.
|
|
711
|
+
* @returns true if console logging is enabled
|
|
712
|
+
*/
|
|
713
|
+
export function isConsoleLoggingEnabled(): boolean;
|
|
@@ -455,7 +455,8 @@ void RatsClient::handle_client(socket_t client_socket, const std::string& peer_h
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
// ----- 2. DECRYPT IF NEEDED -----
|
|
458
|
-
|
|
458
|
+
// Use vector directly to avoid unnecessary string conversions
|
|
459
|
+
std::vector<uint8_t> data;
|
|
459
460
|
|
|
460
461
|
if (noise_handshake_done) {
|
|
461
462
|
std::string current_peer_id;
|
|
@@ -492,16 +493,18 @@ void RatsClient::handle_client(socket_t client_socket, const std::string& peer_h
|
|
|
492
493
|
}
|
|
493
494
|
|
|
494
495
|
plaintext.resize(pt_len);
|
|
495
|
-
data = std::
|
|
496
|
+
data = std::move(plaintext);
|
|
496
497
|
LOG_CLIENT_DEBUG("Decrypted message from " << current_peer_id << " (" << pt_len << " bytes)");
|
|
497
498
|
} else {
|
|
498
|
-
data = std::
|
|
499
|
+
data = std::move(received_bytes);
|
|
499
500
|
}
|
|
500
501
|
} else {
|
|
501
|
-
data = std::
|
|
502
|
+
data = std::move(received_bytes);
|
|
502
503
|
}
|
|
503
504
|
|
|
504
|
-
|
|
505
|
+
// Log first 50 bytes for debugging
|
|
506
|
+
size_t log_len = (std::min)(data.size(), static_cast<size_t>(50));
|
|
507
|
+
LOG_CLIENT_DEBUG("Received data from " << peer_hash_id << ": " << std::string(data.begin(), data.begin() + log_len) << (data.size() > 50 ? "..." : ""));
|
|
505
508
|
|
|
506
509
|
// ----- 3. CONNECTION STATE CHECK (during handshake phase only) -----
|
|
507
510
|
if (!handshake_completed) {
|
|
@@ -531,51 +534,75 @@ void RatsClient::handle_client(socket_t client_socket, const std::string& peer_h
|
|
|
531
534
|
}
|
|
532
535
|
|
|
533
536
|
// ----- 4. HANDSHAKE PHASE -----
|
|
534
|
-
|
|
537
|
+
// Only check for handshake messages BEFORE handshake is completed
|
|
538
|
+
// This avoids expensive JSON parsing on every message after handshake
|
|
539
|
+
if (!handshake_completed && is_handshake_message(data)) {
|
|
535
540
|
if (!handle_handshake_message(client_socket, peer_hash_id, data)) {
|
|
536
541
|
LOG_CLIENT_ERROR("Failed to handle handshake message from " << peer_hash_id);
|
|
537
542
|
break;
|
|
538
543
|
}
|
|
539
544
|
|
|
540
|
-
// Check if handshake just completed
|
|
545
|
+
// Check if rats handshake just completed (COMPLETED or NOISE_PENDING state)
|
|
541
546
|
if (!handshake_completed) {
|
|
542
547
|
RatsPeer peer_copy;
|
|
543
|
-
bool
|
|
548
|
+
bool rats_handshake_done = false;
|
|
549
|
+
bool needs_noise_handshake = false;
|
|
544
550
|
|
|
545
551
|
{
|
|
546
552
|
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
547
553
|
auto sock_it = socket_to_peer_id_.find(client_socket);
|
|
548
554
|
if (sock_it != socket_to_peer_id_.end()) {
|
|
549
555
|
auto peer_it = peers_.find(sock_it->second);
|
|
550
|
-
if (peer_it != peers_.end()
|
|
551
|
-
|
|
552
|
-
|
|
556
|
+
if (peer_it != peers_.end()) {
|
|
557
|
+
// Check if rats handshake completed (either COMPLETED or NOISE_PENDING)
|
|
558
|
+
if (peer_it->second.is_handshake_completed()) {
|
|
559
|
+
rats_handshake_done = true;
|
|
560
|
+
peer_copy = peer_it->second;
|
|
561
|
+
} else if (peer_it->second.handshake_state == RatsPeer::HandshakeState::NOISE_PENDING) {
|
|
562
|
+
rats_handshake_done = true;
|
|
563
|
+
needs_noise_handshake = true;
|
|
564
|
+
peer_copy = peer_it->second;
|
|
565
|
+
}
|
|
553
566
|
}
|
|
554
567
|
}
|
|
555
568
|
}
|
|
556
569
|
|
|
557
570
|
// ----- POST-HANDSHAKE ACTIONS -----
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
LOG_CLIENT_INFO("Handshake completed for peer " << peer_hash_id << " (peer_id: " << peer_copy.peer_id << ")");
|
|
571
|
+
if (rats_handshake_done) {
|
|
572
|
+
LOG_CLIENT_INFO("Rats handshake completed for peer " << peer_hash_id << " (peer_id: " << peer_copy.peer_id << ")");
|
|
561
573
|
|
|
562
574
|
// Remove from reconnection queue if present (successful connection)
|
|
563
575
|
remove_from_reconnect_queue(peer_copy.peer_id);
|
|
564
576
|
|
|
565
577
|
// Noise encryption handshake - only if BOTH sides support encryption
|
|
566
578
|
// peer_copy.encryption_enabled is already negotiated in handle_handshake_message()
|
|
567
|
-
if (
|
|
579
|
+
if (needs_noise_handshake) {
|
|
568
580
|
LOG_CLIENT_INFO("Starting Noise handshake for peer " << peer_copy.peer_id);
|
|
569
581
|
if (perform_noise_handshake(client_socket, peer_copy.peer_id, peer_copy.is_outgoing)) {
|
|
570
582
|
noise_handshake_done = true;
|
|
571
583
|
LOG_CLIENT_INFO("Noise handshake successful for peer " << peer_copy.peer_id);
|
|
584
|
+
|
|
585
|
+
// Update state to COMPLETED after successful Noise handshake
|
|
586
|
+
{
|
|
587
|
+
std::lock_guard<std::mutex> lock(peers_mutex_);
|
|
588
|
+
auto sock_it = socket_to_peer_id_.find(client_socket);
|
|
589
|
+
if (sock_it != socket_to_peer_id_.end()) {
|
|
590
|
+
auto peer_it = peers_.find(sock_it->second);
|
|
591
|
+
if (peer_it != peers_.end()) {
|
|
592
|
+
peer_it->second.handshake_state = RatsPeer::HandshakeState::COMPLETED;
|
|
593
|
+
log_handshake_completion_unlocked(peer_it->second);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
572
597
|
} else {
|
|
573
598
|
LOG_CLIENT_ERROR("Noise handshake failed for peer " << peer_copy.peer_id);
|
|
599
|
+
// Connection will be closed due to failed Noise handshake
|
|
600
|
+
break;
|
|
574
601
|
}
|
|
575
|
-
} else {
|
|
576
|
-
LOG_CLIENT_DEBUG("Skipping Noise handshake for peer " << peer_copy.peer_id << " - encryption not negotiated");
|
|
577
602
|
}
|
|
578
603
|
|
|
604
|
+
handshake_completed = true;
|
|
605
|
+
|
|
579
606
|
// Connection callback
|
|
580
607
|
if (connection_callback_) {
|
|
581
608
|
connection_callback_(client_socket, peer_copy.peer_id);
|
|
@@ -621,11 +648,11 @@ void RatsClient::handle_client(socket_t client_socket, const std::string& peer_h
|
|
|
621
648
|
continue;
|
|
622
649
|
}
|
|
623
650
|
|
|
624
|
-
|
|
651
|
+
// Use data directly - no need for extra copy
|
|
625
652
|
MessageHeader header;
|
|
626
653
|
std::vector<uint8_t> payload;
|
|
627
654
|
|
|
628
|
-
if (!parse_message_with_header(
|
|
655
|
+
if (!parse_message_with_header(data, header, payload)) {
|
|
629
656
|
LOG_CLIENT_WARN("No header found in message from " << peer_hash_id);
|
|
630
657
|
continue;
|
|
631
658
|
}
|
|
@@ -741,14 +768,15 @@ std::string RatsClient::create_handshake_message(const std::string& message_type
|
|
|
741
768
|
handshake_msg["message_type"] = message_type;
|
|
742
769
|
handshake_msg["timestamp"] = timestamp;
|
|
743
770
|
handshake_msg["encryption_enabled"] = is_encryption_enabled();
|
|
771
|
+
handshake_msg["listen_port"] = listen_port_;
|
|
744
772
|
|
|
745
773
|
return handshake_msg.dump();
|
|
746
774
|
}
|
|
747
775
|
|
|
748
|
-
bool RatsClient::parse_handshake_message(const std::
|
|
776
|
+
bool RatsClient::parse_handshake_message(const std::vector<uint8_t>& data, HandshakeMessage& out_msg) const {
|
|
749
777
|
try {
|
|
750
|
-
// Use nlohmann::json
|
|
751
|
-
nlohmann::json json_msg = nlohmann::json::parse(
|
|
778
|
+
// Use nlohmann::json with iterators to avoid string conversion
|
|
779
|
+
nlohmann::json json_msg = nlohmann::json::parse(data.begin(), data.end());
|
|
752
780
|
|
|
753
781
|
// Clear the output structure
|
|
754
782
|
out_msg = HandshakeMessage{};
|
|
@@ -762,6 +790,8 @@ bool RatsClient::parse_handshake_message(const std::string& message, HandshakeMe
|
|
|
762
790
|
out_msg.timestamp = json_msg.value("timestamp", static_cast<int64_t>(0));
|
|
763
791
|
// Parse encryption_enabled (default to false for backward compatibility)
|
|
764
792
|
out_msg.encryption_enabled = json_msg.value("encryption_enabled", false);
|
|
793
|
+
// Parse listen_port (default to 0 for backward compatibility with older clients)
|
|
794
|
+
out_msg.listen_port = json_msg.value("listen_port", static_cast<uint16_t>(0));
|
|
765
795
|
|
|
766
796
|
return true;
|
|
767
797
|
|
|
@@ -823,37 +853,30 @@ bool RatsClient::validate_handshake_message(const HandshakeMessage& msg) const {
|
|
|
823
853
|
return true;
|
|
824
854
|
}
|
|
825
855
|
|
|
826
|
-
bool RatsClient::is_handshake_message(const std::
|
|
856
|
+
bool RatsClient::is_handshake_message(const std::vector<uint8_t>& data) const {
|
|
827
857
|
try {
|
|
828
|
-
std::string json_to_parse = message;
|
|
829
|
-
|
|
830
858
|
// Check if message has our message header (starts with "RATS" magic)
|
|
831
|
-
std::vector<uint8_t> message_data(message.begin(), message.end());
|
|
832
859
|
MessageHeader header;
|
|
833
860
|
std::vector<uint8_t> payload;
|
|
834
861
|
|
|
835
|
-
if (parse_message_with_header(
|
|
862
|
+
if (parse_message_with_header(data, header, payload)) {
|
|
836
863
|
// Message has valid header - extract the JSON payload
|
|
837
864
|
if (header.type == MessageDataType::STRING || header.type == MessageDataType::JSON) {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
865
|
+
// Parse the JSON message directly from payload
|
|
866
|
+
nlohmann::json json_msg = nlohmann::json::parse(payload.begin(), payload.end());
|
|
867
|
+
std::string expected_protocol;
|
|
868
|
+
{
|
|
869
|
+
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
870
|
+
expected_protocol = custom_protocol_name_;
|
|
871
|
+
}
|
|
872
|
+
return json_msg.value("protocol", "") == expected_protocol &&
|
|
873
|
+
json_msg.value("message_type", "") == "handshake";
|
|
842
874
|
}
|
|
843
|
-
|
|
844
|
-
// Message has no header
|
|
875
|
+
// Handshake messages should be string/JSON type
|
|
845
876
|
return false;
|
|
846
877
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
nlohmann::json json_msg = nlohmann::json::parse(json_to_parse);
|
|
850
|
-
std::string expected_protocol;
|
|
851
|
-
{
|
|
852
|
-
std::lock_guard<std::mutex> lock(protocol_config_mutex_);
|
|
853
|
-
expected_protocol = custom_protocol_name_;
|
|
854
|
-
}
|
|
855
|
-
return json_msg.value("protocol", "") == expected_protocol &&
|
|
856
|
-
json_msg.value("message_type", "") == "handshake";
|
|
878
|
+
// Message has no header
|
|
879
|
+
return false;
|
|
857
880
|
} catch (const std::exception&) {
|
|
858
881
|
return false;
|
|
859
882
|
}
|
|
@@ -898,28 +921,25 @@ bool RatsClient::send_handshake(socket_t socket, const std::string& our_peer_id)
|
|
|
898
921
|
return send_handshake_unlocked(socket, our_peer_id);
|
|
899
922
|
}
|
|
900
923
|
|
|
901
|
-
bool RatsClient::handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::
|
|
902
|
-
// Extract JSON payload from message header
|
|
903
|
-
std::string json_to_parse = message;
|
|
904
|
-
std::vector<uint8_t> message_data(message.begin(), message.end());
|
|
924
|
+
bool RatsClient::handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::vector<uint8_t>& data) {
|
|
925
|
+
// Extract JSON payload from message header
|
|
905
926
|
MessageHeader header;
|
|
906
927
|
std::vector<uint8_t> payload;
|
|
907
928
|
|
|
908
|
-
if (parse_message_with_header(
|
|
909
|
-
// Message has valid header - extract the JSON payload
|
|
910
|
-
if (header.type == MessageDataType::STRING || header.type == MessageDataType::JSON) {
|
|
911
|
-
json_to_parse = std::string(payload.begin(), payload.end());
|
|
912
|
-
} else {
|
|
913
|
-
LOG_CLIENT_ERROR("Invalid message type for handshake: " << static_cast<int>(header.type));
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
} else {
|
|
929
|
+
if (!parse_message_with_header(data, header, payload)) {
|
|
917
930
|
LOG_CLIENT_ERROR("Failed to parse handshake message header from " << peer_hash_id);
|
|
918
931
|
return false;
|
|
919
932
|
}
|
|
920
933
|
|
|
934
|
+
// Message has valid header - check the type
|
|
935
|
+
if (header.type != MessageDataType::STRING && header.type != MessageDataType::JSON) {
|
|
936
|
+
LOG_CLIENT_ERROR("Invalid message type for handshake: " << static_cast<int>(header.type));
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// Parse handshake message directly from payload (no string conversion)
|
|
921
941
|
HandshakeMessage handshake_msg;
|
|
922
|
-
if (!parse_handshake_message(
|
|
942
|
+
if (!parse_handshake_message(payload, handshake_msg)) {
|
|
923
943
|
LOG_CLIENT_ERROR("Failed to parse handshake message from " << peer_hash_id);
|
|
924
944
|
return false;
|
|
925
945
|
}
|
|
@@ -995,12 +1015,35 @@ bool RatsClient::handle_handshake_message(socket_t socket, const std::string& pe
|
|
|
995
1015
|
<< ", remote=" << remote_encryption
|
|
996
1016
|
<< ", result=" << peer.encryption_enabled);
|
|
997
1017
|
|
|
1018
|
+
// For incoming connections, update port to the peer's actual listen port
|
|
1019
|
+
// This is critical for peer exchange to work correctly
|
|
1020
|
+
if (!peer.is_outgoing && handshake_msg.listen_port > 0) {
|
|
1021
|
+
// Remove old address mapping
|
|
1022
|
+
address_to_peer_id_.erase(peer.normalized_address);
|
|
1023
|
+
|
|
1024
|
+
// Update port and normalized address
|
|
1025
|
+
peer.port = handshake_msg.listen_port;
|
|
1026
|
+
peer.normalized_address = normalize_peer_address(peer.ip, peer.port);
|
|
1027
|
+
|
|
1028
|
+
// Add new address mapping
|
|
1029
|
+
address_to_peer_id_[peer.normalized_address] = peer.peer_id;
|
|
1030
|
+
|
|
1031
|
+
LOG_CLIENT_INFO("Updated incoming peer port to listen_port: " << peer.ip << ":" << peer.port);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
998
1034
|
// Simplified handshake logic - just one message type
|
|
999
1035
|
if (peer.handshake_state == RatsPeer::HandshakeState::PENDING) {
|
|
1000
1036
|
// This is an incoming handshake - send our handshake back
|
|
1001
1037
|
if (send_handshake_unlocked(socket, get_our_peer_id())) {
|
|
1002
|
-
|
|
1003
|
-
|
|
1038
|
+
// If encryption is enabled, we need to do Noise handshake first
|
|
1039
|
+
// Set NOISE_PENDING to prevent other threads from sending messages
|
|
1040
|
+
if (peer.encryption_enabled) {
|
|
1041
|
+
peer.handshake_state = RatsPeer::HandshakeState::NOISE_PENDING;
|
|
1042
|
+
LOG_CLIENT_DEBUG("Rats handshake done, entering NOISE_PENDING state for " << peer_hash_id);
|
|
1043
|
+
} else {
|
|
1044
|
+
peer.handshake_state = RatsPeer::HandshakeState::COMPLETED;
|
|
1045
|
+
log_handshake_completion_unlocked(peer);
|
|
1046
|
+
}
|
|
1004
1047
|
|
|
1005
1048
|
// Append to historical peers file after successful connection
|
|
1006
1049
|
append_peer_to_historical_file(peer);
|
|
@@ -1013,8 +1056,15 @@ bool RatsClient::handle_handshake_message(socket_t socket, const std::string& pe
|
|
|
1013
1056
|
}
|
|
1014
1057
|
} else if (peer.handshake_state == RatsPeer::HandshakeState::SENT) {
|
|
1015
1058
|
// This is a response to our handshake
|
|
1016
|
-
|
|
1017
|
-
|
|
1059
|
+
// If encryption is enabled, we need to do Noise handshake first
|
|
1060
|
+
// Set NOISE_PENDING to prevent other threads from sending messages
|
|
1061
|
+
if (peer.encryption_enabled) {
|
|
1062
|
+
peer.handshake_state = RatsPeer::HandshakeState::NOISE_PENDING;
|
|
1063
|
+
LOG_CLIENT_DEBUG("Rats handshake done, entering NOISE_PENDING state for " << peer_hash_id);
|
|
1064
|
+
} else {
|
|
1065
|
+
peer.handshake_state = RatsPeer::HandshakeState::COMPLETED;
|
|
1066
|
+
log_handshake_completion_unlocked(peer);
|
|
1067
|
+
}
|
|
1018
1068
|
|
|
1019
1069
|
// Append to historical peers file after successful connection
|
|
1020
1070
|
append_peer_to_historical_file(peer);
|
|
@@ -2377,8 +2427,8 @@ nlohmann::json RatsClient::create_peer_exchange_message(const RatsPeer& peer) {
|
|
|
2377
2427
|
payload["peer_id"] = peer.peer_id;
|
|
2378
2428
|
payload["connection_type"] = peer.is_outgoing ? "outgoing" : "incoming";
|
|
2379
2429
|
|
|
2380
|
-
// Create rats message
|
|
2381
|
-
return create_rats_message("peer", payload,
|
|
2430
|
+
// Create rats message - use OUR peer_id as sender, not the advertised peer's id
|
|
2431
|
+
return create_rats_message("peer", payload, get_our_peer_id());
|
|
2382
2432
|
}
|
|
2383
2433
|
|
|
2384
2434
|
void RatsClient::broadcast_peer_exchange_message(const RatsPeer& new_peer) {
|
package/native-src/src/librats.h
CHANGED
|
@@ -48,9 +48,10 @@ struct RatsPeer {
|
|
|
48
48
|
// Handshake-related fields
|
|
49
49
|
enum class HandshakeState {
|
|
50
50
|
PENDING, // Handshake not started
|
|
51
|
-
SENT,
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
@@ -990,6 +991,20 @@ public:
|
|
|
990
991
|
// Logging Control API
|
|
991
992
|
// =========================================================================
|
|
992
993
|
|
|
994
|
+
/**
|
|
995
|
+
* Enable or disable console logging
|
|
996
|
+
* When disabled, log messages will not be printed to stdout/stderr
|
|
997
|
+
* File logging (if enabled) will still work
|
|
998
|
+
* @param enabled Whether to enable console logging (default: true)
|
|
999
|
+
*/
|
|
1000
|
+
void set_console_logging_enabled(bool enabled);
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Check if console logging is currently enabled
|
|
1004
|
+
* @return true if console logging is enabled
|
|
1005
|
+
*/
|
|
1006
|
+
bool is_console_logging_enabled() const;
|
|
1007
|
+
|
|
993
1008
|
/**
|
|
994
1009
|
* Enable or disable file logging
|
|
995
1010
|
* When enabled, logs will be written to "rats.log" by default
|
|
@@ -2046,15 +2061,16 @@ private:
|
|
|
2046
2061
|
std::string message_type;
|
|
2047
2062
|
int64_t timestamp;
|
|
2048
2063
|
bool encryption_enabled; // Whether peer supports/wants encryption
|
|
2064
|
+
uint16_t listen_port; // Peer's listening port for peer exchange
|
|
2049
2065
|
};
|
|
2050
2066
|
|
|
2051
2067
|
std::string create_handshake_message(const std::string& message_type, const std::string& our_peer_id) const;
|
|
2052
|
-
bool parse_handshake_message(const std::
|
|
2068
|
+
bool parse_handshake_message(const std::vector<uint8_t>& data, HandshakeMessage& out_msg) const;
|
|
2053
2069
|
bool validate_handshake_message(const HandshakeMessage& msg) const;
|
|
2054
|
-
bool is_handshake_message(const std::
|
|
2070
|
+
bool is_handshake_message(const std::vector<uint8_t>& data) const;
|
|
2055
2071
|
bool send_handshake(socket_t socket, const std::string& our_peer_id);
|
|
2056
2072
|
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::
|
|
2073
|
+
bool handle_handshake_message(socket_t socket, const std::string& peer_hash_id, const std::vector<uint8_t>& data);
|
|
2058
2074
|
void check_handshake_timeouts();
|
|
2059
2075
|
void log_handshake_completion_unlocked(const RatsPeer& peer);
|
|
2060
2076
|
|
|
@@ -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);
|
|
@@ -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);
|
|
@@ -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
|
|
package/native-src/src/logger.h
CHANGED
|
@@ -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_);
|
|
@@ -147,13 +158,15 @@ public:
|
|
|
147
158
|
// Add message
|
|
148
159
|
console_oss << " " << message << std::endl;
|
|
149
160
|
|
|
150
|
-
// Output to appropriate console stream
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
// Output to appropriate console stream (if console logging is enabled)
|
|
162
|
+
if (console_logging_enabled_) {
|
|
163
|
+
if (level >= LogLevel::ERROR) {
|
|
164
|
+
std::cerr << console_oss.str();
|
|
165
|
+
std::cerr.flush();
|
|
166
|
+
} else {
|
|
167
|
+
std::cout << console_oss.str();
|
|
168
|
+
std::cout.flush();
|
|
169
|
+
}
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
// Also write to file if file logging is enabled
|
|
@@ -164,8 +177,8 @@ public:
|
|
|
164
177
|
|
|
165
178
|
private:
|
|
166
179
|
Logger() : min_level_(LogLevel::INFO), colors_enabled_(true), timestamps_enabled_(true),
|
|
167
|
-
|
|
168
|
-
max_log_files_(5), current_file_size_(0) {
|
|
180
|
+
console_logging_enabled_(true), file_logging_enabled_(false),
|
|
181
|
+
max_log_file_size_(10 * 1024 * 1024), max_log_files_(5), current_file_size_(0) {
|
|
169
182
|
// Check if we're outputting to a terminal
|
|
170
183
|
is_terminal_ = isatty(fileno(stdout));
|
|
171
184
|
|
|
@@ -366,6 +379,9 @@ private:
|
|
|
366
379
|
bool timestamps_enabled_;
|
|
367
380
|
bool is_terminal_;
|
|
368
381
|
|
|
382
|
+
// Console logging control
|
|
383
|
+
bool console_logging_enabled_;
|
|
384
|
+
|
|
369
385
|
// File logging members
|
|
370
386
|
bool file_logging_enabled_;
|
|
371
387
|
std::string log_file_path_;
|
package/package.json
CHANGED
package/src/librats_node.cpp
CHANGED
|
@@ -1330,6 +1330,25 @@ Napi::Value GetVersion(const Napi::CallbackInfo& info) {
|
|
|
1330
1330
|
return version;
|
|
1331
1331
|
}
|
|
1332
1332
|
|
|
1333
|
+
// Console logging control functions
|
|
1334
|
+
Napi::Value SetConsoleLoggingEnabled(const Napi::CallbackInfo& info) {
|
|
1335
|
+
Napi::Env env = info.Env();
|
|
1336
|
+
|
|
1337
|
+
if (info.Length() < 1 || !info[0].IsBoolean()) {
|
|
1338
|
+
Napi::TypeError::New(env, "Boolean expected").ThrowAsJavaScriptException();
|
|
1339
|
+
return env.Undefined();
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
bool enabled = info[0].As<Napi::Boolean>().Value();
|
|
1343
|
+
rats_set_console_logging_enabled(enabled ? 1 : 0);
|
|
1344
|
+
return env.Undefined();
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
Napi::Value IsConsoleLoggingEnabled(const Napi::CallbackInfo& info) {
|
|
1348
|
+
Napi::Env env = info.Env();
|
|
1349
|
+
return Napi::Boolean::New(env, rats_is_console_logging_enabled() != 0);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1333
1352
|
Napi::Value GetGitDescribe(const Napi::CallbackInfo& info) {
|
|
1334
1353
|
Napi::Env env = info.Env();
|
|
1335
1354
|
const char* git_describe = rats_get_git_describe();
|
|
@@ -1399,6 +1418,10 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
1399
1418
|
exports.Set("getGitDescribe", Napi::Function::New(env, GetGitDescribe));
|
|
1400
1419
|
exports.Set("getAbi", Napi::Function::New(env, GetAbi));
|
|
1401
1420
|
|
|
1421
|
+
// Export logging control functions
|
|
1422
|
+
exports.Set("setConsoleLoggingEnabled", Napi::Function::New(env, SetConsoleLoggingEnabled));
|
|
1423
|
+
exports.Set("isConsoleLoggingEnabled", Napi::Function::New(env, IsConsoleLoggingEnabled));
|
|
1424
|
+
|
|
1402
1425
|
// Export constants
|
|
1403
1426
|
exports.Set("constants", InitConstants(env));
|
|
1404
1427
|
|