Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Monero onion node test #962

Merged
merged 2 commits into from
Jul 31, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 92 additions & 83 deletions lib/utilities/test_monero_node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,93 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
int port,
})? proxyInfo,
}) async {
final httpClient = HttpClient();
MoneroNodeConnectionResponse? badCertResponse;

try {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
ProxySettings(
proxyInfo.host,
proxyInfo.port,
),
]);
if (uri.host.endsWith(".onion")) {
if (proxyInfo == null) {
// If the host ends in .onion, we can't access it without Tor.
return MoneroNodeConnectionResponse(null, null, null, false);
}

httpClient.badCertificateCallback = (cert, url, port) {
if (allowBadX509Certificate) {
return true;
SOCKSSocket? socket;
try {
// An HttpClient cannot be used for onion nodes.
//
// The SOCKSSocket class from the tor_ffi_plugin package can be used to
// connect to .onion addresses. We'll do the same things as above but
// with SOCKSSocket instead of httpClient.
socket = await SOCKSSocket.create(
proxyHost: proxyInfo.host.address,
proxyPort: proxyInfo.port,
sslEnabled: false,
);
await socket.connect();
await socket.connectTo(uri.host, uri.port);

final body = jsonEncode({
"jsonrpc": "2.0",
"id": "0",
"method": "get_info",
});

final request = 'POST /json_rpc HTTP/1.1\r\n'
'Host: ${uri.host}\r\n'
'Content-Type: application/json\r\n'
'Content-Length: ${body.length}\r\n'
'\r\n'
'$body';

socket.write(request);
print("Request sent: $request");

final buffer = StringBuffer();
await for (var response in socket.inputStream) {
buffer.write(utf8.decode(response));
if (buffer.toString().contains("\r\n\r\n")) {
break;
}
}

if (badCertResponse == null) {
badCertResponse = MoneroNodeConnectionResponse(cert, url, port, false);
} else {
return false;
final result = buffer.toString();
print("Response received: $result");

// Check if the response contains "results" and does not contain "error"
final success =
result.contains('"result":') && !result.contains('"error"');

return MoneroNodeConnectionResponse(null, null, null, success);
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
return MoneroNodeConnectionResponse(null, null, null, false);
} finally {
await socket?.close();
}
} else {
final httpClient = HttpClient();
MoneroNodeConnectionResponse? badCertResponse;
try {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
ProxySettings(
proxyInfo.host,
proxyInfo.port,
),
]);
}

return false;
};
httpClient.badCertificateCallback = (cert, url, port) {
if (allowBadX509Certificate) {
return true;
}

if (badCertResponse == null) {
badCertResponse =
MoneroNodeConnectionResponse(cert, url, port, false);
} else {
return false;
}

return false;
};

if (!uri.host.endsWith(".onion")) {
final request = await httpClient.postUrl(uri);

final body = utf8.encode(
Expand All @@ -91,72 +150,22 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(

final response = await request.close();
final result = await response.transform(utf8.decoder).join();
// TODO: json decoded without error so assume connection exists?
// or we can check for certain values in the response to decide
return MoneroNodeConnectionResponse(null, null, null, true);
} else {
if (proxyInfo == null) {
// If the host ends in .onion, we can't access it without Tor.
return MoneroNodeConnectionResponse(null, null, null, false);
}
print("HTTP Response: $result");

// An HttpClient cannot be used for onion nodes.
//
// The SOCKSSocket class from the tor_ffi_plugin package can be used to
// connect to .onion addresses. We'll do the same things as above but
// with SOCKSSocket instead of httpClient.
final socket = await SOCKSSocket.create(
proxyHost: proxyInfo.host.address,
proxyPort: proxyInfo.port,
sslEnabled: false,
);
await socket.connect();
await socket.connectTo(uri.host, uri.port);
final success =
result.contains('"result":') && !result.contains('"error"');

final body = utf8.encode(
jsonEncode({
"jsonrpc": "2.0",
"id": "0",
"method": "get_info",
}),
);

// Write the request body to the socket.
socket.write(body);

// If this is an onion address and there are no errors yet, assume success.
if (uri.host.endsWith('.onion')) {
return MoneroNodeConnectionResponse(null, null, null, true);
return MoneroNodeConnectionResponse(null, null, null, success);
} catch (e, s) {
if (badCertResponse != null) {
return badCertResponse!;
} else {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
return MoneroNodeConnectionResponse(null, null, null, false);
}

// Read the response.
final response = await socket.inputStream.first;
final result = utf8.decode(response);

// Close the socket.
await socket.close();
return MoneroNodeConnectionResponse(null, null, null, true);

// Parse the response.
//
// This is commented because any issues should throw.
// final Map<String, dynamic> jsonResponse = jsonDecode(result);
// print(jsonResponse);
// if (jsonResponse.containsKey('result')) {
// return MoneroNodeConnectionResponse(null, null, null, true);
// } else {
// return MoneroNodeConnectionResponse(null, null, null, false);
// }
}
} catch (e, s) {
if (badCertResponse != null) {
return badCertResponse!;
} else {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
return MoneroNodeConnectionResponse(null, null, null, false);
} finally {
httpClient.close(force: true);
}
} finally {
httpClient.close(force: true);
}
}

Expand Down
Loading