Skip to content

Commit 9192f02

Browse files
Dendi Suhubdyclaude
andcommitted
Implement TOML config parser and fix CLI arg override for obfs4 activation
The TOML parser was stubbed (returned default Config ignoring file content), preventing bridge transport config from being read. Implemented full parser that reads all sections including [bridge.transport] for obfs4. Changed CLI args to std::optional so config file values are preserved when not overridden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 588b0d3 commit 9192f02

File tree

2 files changed

+199
-19
lines changed

2 files changed

+199
-19
lines changed

src/main.cpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ void setup_signal_handlers() {
3232

3333
struct CommandLineArgs {
3434
std::string config_file;
35-
tor::modes::RelayMode mode{tor::modes::RelayMode::Middle};
36-
uint16_t or_port{9001};
37-
uint16_t dir_port{0};
35+
std::optional<tor::modes::RelayMode> mode;
36+
std::optional<uint16_t> or_port;
37+
std::optional<uint16_t> dir_port;
3838
std::string nickname;
3939
std::string data_dir;
4040
bool foreground{false};
@@ -115,7 +115,7 @@ std::optional<CommandLineArgs> parse_args(int argc, char* argv[]) {
115115
std::cerr << "Error: Invalid mode '" << value << "'. Use: middle, exit, bridge, or guard\n";
116116
return std::nullopt;
117117
}
118-
args.mode = *mode_result;
118+
args.mode = mode_result.value();
119119
} else if (arg == "-p" || arg == "--port") {
120120
try {
121121
int port = std::stoi(value);
@@ -179,10 +179,10 @@ tor::util::Config create_config(const CommandLineArgs& args) {
179179
}
180180
}
181181

182-
// Override with command-line arguments
183-
config.relay.mode = args.mode;
184-
config.relay.or_port = args.or_port;
185-
config.relay.dir_port = args.dir_port;
182+
// Override with command-line arguments (only if explicitly provided)
183+
if (args.mode) config.relay.mode = *args.mode;
184+
if (args.or_port) config.relay.or_port = *args.or_port;
185+
if (args.dir_port) config.relay.dir_port = *args.dir_port;
186186

187187
if (!args.nickname.empty()) {
188188
config.relay.nickname = args.nickname;
@@ -229,13 +229,16 @@ int main(int argc, char* argv[]) {
229229
logger.set_level(args.log_level);
230230
logger.add_sink(std::make_shared<tor::util::ConsoleSink>());
231231

232-
LOG_INFO("Starting Tor Relay...");
233-
LOG_INFO("Mode: {}", tor::modes::relay_mode_name(args.mode));
234-
LOG_INFO("OR Port: {}", args.or_port);
235-
236232
// Create configuration
237233
auto config = create_config(args);
238234

235+
LOG_INFO("Starting Tor Relay...");
236+
LOG_INFO("Mode: {}", tor::modes::relay_mode_name(config.relay.mode));
237+
LOG_INFO("OR Port: {}", config.relay.or_port);
238+
if (!config.bridge.transport.empty()) {
239+
LOG_INFO("Transport: {} on port {}", config.bridge.transport, config.bridge.transport_port);
240+
}
241+
239242
// Ensure data directory exists
240243
try {
241244
ensure_data_directory(config.relay.data_dir.string());

src/util/config.cpp

Lines changed: 184 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,97 @@
1-
// Implementation - util/config.cpp
21
#include "tor/util/config.hpp"
2+
#include <algorithm>
33
#include <fstream>
44
#include <iostream>
55
#include <sstream>
6+
#include <unordered_map>
67

78
namespace tor::util {
89

10+
// --- Minimal TOML parser (standard-library only) ---
11+
12+
namespace {
13+
14+
std::string trim(const std::string& s) {
15+
auto start = s.find_first_not_of(" \t\r\n");
16+
if (start == std::string::npos) return "";
17+
auto end = s.find_last_not_of(" \t\r\n");
18+
return s.substr(start, end - start + 1);
19+
}
20+
21+
std::string unquote(const std::string& s) {
22+
if (s.size() >= 2 && s.front() == '"' && s.back() == '"') {
23+
return s.substr(1, s.size() - 2);
24+
}
25+
return s;
26+
}
27+
28+
// Flat key-value store: "section.subsection.key" -> "value"
29+
using TomlMap = std::unordered_map<std::string, std::string>;
30+
31+
TomlMap parse_toml_simple(const std::string& content) {
32+
TomlMap result;
33+
std::string current_section;
34+
std::istringstream stream(content);
35+
std::string line;
36+
37+
while (std::getline(stream, line)) {
38+
line = trim(line);
39+
40+
// Skip empty lines and comments
41+
if (line.empty() || line[0] == '#') continue;
42+
43+
// Section header: [section] or [section.subsection]
44+
if (line.front() == '[' && line.back() == ']') {
45+
current_section = line.substr(1, line.size() - 2);
46+
current_section = trim(current_section);
47+
continue;
48+
}
49+
50+
// Key = value
51+
auto eq = line.find('=');
52+
if (eq == std::string::npos) continue;
53+
54+
auto key = trim(line.substr(0, eq));
55+
auto value = trim(line.substr(eq + 1));
56+
value = unquote(value);
57+
58+
// Build fully qualified key
59+
std::string fqkey = current_section.empty() ? key : current_section + "." + key;
60+
result[fqkey] = value;
61+
}
62+
63+
return result;
64+
}
65+
66+
std::string get(const TomlMap& m, const std::string& key, const std::string& def = "") {
67+
auto it = m.find(key);
68+
return it != m.end() ? it->second : def;
69+
}
70+
71+
int get_int(const TomlMap& m, const std::string& key, int def = 0) {
72+
auto it = m.find(key);
73+
if (it == m.end()) return def;
74+
try { return std::stoi(it->second); } catch (...) { return def; }
75+
}
76+
77+
bool get_bool(const TomlMap& m, const std::string& key, bool def = false) {
78+
auto it = m.find(key);
79+
if (it == m.end()) return def;
80+
return it->second == "true" || it->second == "1";
81+
}
82+
83+
modes::BridgeRelay::Distribution parse_distribution(const std::string& s) {
84+
if (s == "https") return modes::BridgeRelay::Distribution::Https;
85+
if (s == "email") return modes::BridgeRelay::Distribution::Email;
86+
if (s == "moat") return modes::BridgeRelay::Distribution::Moat;
87+
if (s == "any") return modes::BridgeRelay::Distribution::Any;
88+
return modes::BridgeRelay::Distribution::None;
89+
}
90+
91+
} // namespace
92+
93+
// --- Config implementation ---
94+
995
std::expected<Config, ConfigError> Config::load_from_file(const std::filesystem::path& path) {
1096
if (!std::filesystem::exists(path)) {
1197
return std::unexpected(ConfigError::FileNotFound);
@@ -18,14 +104,105 @@ std::expected<Config, ConfigError> Config::load_from_file(const std::filesystem:
18104

19105
std::stringstream buffer;
20106
buffer << file.rdbuf();
21-
107+
22108
return load_from_string(buffer.str());
23109
}
24110

25111
std::expected<Config, ConfigError> Config::load_from_string(const std::string& toml_content) {
26112
Config config;
27-
// Basic TOML parsing - in a real implementation, use a TOML library
28-
// For now, return default config
113+
114+
auto m = parse_toml_simple(toml_content);
115+
116+
// [relay]
117+
auto nickname = get(m, "relay.nickname");
118+
if (!nickname.empty()) config.relay.nickname = nickname;
119+
120+
auto mode_str = get(m, "relay.mode");
121+
if (!mode_str.empty()) {
122+
auto mode_result = modes::parse_relay_mode(mode_str);
123+
if (mode_result) {
124+
config.relay.mode = *mode_result;
125+
}
126+
}
127+
128+
auto or_port = get_int(m, "relay.or_port", 0);
129+
if (or_port > 0) config.relay.or_port = static_cast<uint16_t>(or_port);
130+
131+
auto dir_port = get_int(m, "relay.dir_port", -1);
132+
if (dir_port >= 0) config.relay.dir_port = static_cast<uint16_t>(dir_port);
133+
134+
auto contact = get(m, "relay.contact");
135+
if (!contact.empty()) config.relay.contact_info = contact;
136+
137+
auto address = get(m, "relay.address");
138+
if (!address.empty()) config.relay.address = address;
139+
140+
auto bind_address = get(m, "relay.bind_address");
141+
if (!bind_address.empty()) config.relay.address = bind_address;
142+
143+
// [relay.bandwidth]
144+
auto bw_rate = get_int(m, "relay.bandwidth.rate", 0);
145+
auto bw_burst = get_int(m, "relay.bandwidth.burst", 0);
146+
if (bw_rate > 0) {
147+
config.relay.bandwidth.rate_bytes_per_sec = static_cast<size_t>(bw_rate);
148+
}
149+
if (bw_burst > 0) {
150+
config.relay.bandwidth.burst_bytes = static_cast<size_t>(bw_burst);
151+
}
152+
153+
// [bridge]
154+
auto distribution = get(m, "bridge.distribution");
155+
if (!distribution.empty()) {
156+
config.bridge.distribution = parse_distribution(distribution);
157+
}
158+
159+
// [bridge.transport]
160+
auto transport_type = get(m, "bridge.transport.type");
161+
if (!transport_type.empty()) {
162+
config.bridge.transport = transport_type;
163+
}
164+
165+
auto transport_port = get_int(m, "bridge.transport.port", 0);
166+
if (transport_port > 0) {
167+
config.bridge.transport_port = static_cast<uint16_t>(transport_port);
168+
}
169+
170+
auto iat_mode = get_int(m, "bridge.transport.iat_mode", 0);
171+
config.bridge.iat_mode = static_cast<uint8_t>(iat_mode);
172+
173+
// [directory]
174+
auto publish = get(m, "directory.publish_server_descriptor");
175+
if (!publish.empty()) {
176+
config.directory.publish_server_descriptor = (publish == "true" || publish == "1");
177+
}
178+
179+
auto fetch_interval = get_int(m, "directory.fetch_interval", 0);
180+
if (fetch_interval > 0) {
181+
config.directory.publish_interval = std::chrono::seconds(fetch_interval);
182+
}
183+
184+
// [logging]
185+
auto log_level = get(m, "logging.level");
186+
if (!log_level.empty()) config.logging.level = log_level;
187+
188+
auto log_file = get(m, "logging.file");
189+
if (!log_file.empty()) config.logging.log_file = log_file;
190+
191+
// [data]
192+
auto data_dir = get(m, "data.directory");
193+
if (!data_dir.empty()) config.relay.data_dir = data_dir;
194+
195+
// [network]
196+
auto connect_timeout = get_int(m, "network.connect_timeout", 0);
197+
(void)connect_timeout; // Stored in relay runtime, not config
198+
199+
// [security]
200+
auto sandbox = get_bool(m, "security.sandbox", false);
201+
config.security.sandbox_enabled = sandbox;
202+
203+
auto secure_memory = get_bool(m, "security.secure_memory", true);
204+
(void)secure_memory; // Applied at runtime
205+
29206
return config;
30207
}
31208

@@ -55,14 +232,15 @@ std::expected<void, ConfigError> Config::validate() const {
55232
}
56233

57234
void Config::apply_cli_args(int argc, char* argv[]) {
58-
// CLI arg parsing would go here
235+
// CLI arg parsing is handled in main.cpp
236+
(void)argc;
237+
(void)argv;
59238
}
60239

61240
policy::ExitPolicy Config::effective_exit_policy() const {
62241
if (relay.mode == modes::RelayMode::Exit) {
63242
return exit.exit_policy;
64243
}
65-
// Non-exit relays reject all
66244
return policy::ExitPolicy::reject_all();
67245
}
68246

@@ -107,7 +285,6 @@ std::string config_error_message(ConfigError err) {
107285

108286
std::expected<CliArgs, std::string> parse_cli_args(int argc, char* argv[]) {
109287
CliArgs args;
110-
// Basic CLI parsing
111288
for (int i = 1; i < argc; ++i) {
112289
std::string arg = argv[i];
113290
if (arg == "--help" || arg == "-h") {

0 commit comments

Comments
 (0)