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

handle authentication in test of monero nodes #1063

Open
wants to merge 2 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
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
113 changes: 59 additions & 54 deletions lib/utilities/test_monero_node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:http/io_client.dart';
import 'package:monero_rpc/monero_rpc.dart';
import 'package:digest_auth/digest_auth.dart';
import 'package:socks5_proxy/socks.dart';
import 'package:tor_ffi_plugin/socks_socket.dart';

Expand All @@ -27,11 +30,18 @@ class MoneroNodeConnectionResponse {
final int? port;
final bool success;

MoneroNodeConnectionResponse(this.cert, this.url, this.port, this.success);
MoneroNodeConnectionResponse(
this.cert,
this.url,
this.port,
this.success,
);
}

Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
Uri uri,
String? username,
String? password,
bool allowBadX509Certificate, {
required ({
InternetAddress host,
Expand Down Expand Up @@ -59,36 +69,37 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
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;
final rawRequest = DaemonRpc.rawRequestRpc(uri, 'get_info', {});
var response = await socket.send(rawRequest);
// check if we need authentication
String? authenticateHeaderValue;
for (final line in response.split('\r\n')) {
if (line.contains('WWW-authenticate: ')) {
// both the password and username needs to be
if (username == null || password == null) {
// node asking us for authentication, but we don't have any crendentials.
return MoneroNodeConnectionResponse(null, null, null, false);
}
authenticateHeaderValue =
line.replaceFirst('WWW-authenticate: ', '').trim();
}
}

final result = buffer.toString();
print("Response received: $result");
// header to authenticate was present, we need to remake the request with digest
if (authenticateHeaderValue != null) {
final digestAuth = DigestAuth(username!, password!);
digestAuth.initFromAuthorizationHeader(authenticateHeaderValue);

// generate the Authorization header for the second request.
final authHeader = digestAuth.getAuthString('POST', uri.path);
final rawRequestAuthenticated =
DaemonRpc.rawRequestRpc(uri, 'get_info', {}, authHeader);
// resend with an authenticated request
response = await socket.send(rawRequestAuthenticated);
}

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

return MoneroNodeConnectionResponse(null, null, null, success);
} catch (e, s) {
Expand Down Expand Up @@ -124,36 +135,15 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(

return false;
};

final request = await httpClient.postUrl(uri);

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

request.headers.add(
'Content-Length',
body.length.toString(),
preserveHeaderCase: true,
);
request.headers.set(
'Content-Type',
'application/json',
preserveHeaderCase: true,
final daemonRpc = DaemonRpc(
IOClient(httpClient),
'$uri',
username: username,
password: password,
);
final result = await daemonRpc.call('get_info', {});

request.add(body);

final response = await request.close();
final result = await response.transform(utf8.decoder).join();
// print("HTTP Response: $result");

final success =
result.contains('"result":') && !result.contains('"error"');
final success = result.containsKey('status') && result['status'] == 'OK';

return MoneroNodeConnectionResponse(null, null, null, success);
} catch (e, s) {
Expand Down Expand Up @@ -210,3 +200,18 @@ Future<bool> showBadX509CertificateDialog(

return result ?? false;
}

extension on SOCKSSocket {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I should look at adding this to SOCKSSocket itself!

/// write the raw request to the socket and return the response as String
Future<String> send(String rawRequest) async {
write(rawRequest);
final buffer = StringBuffer();
await for (final response in inputStream) {
buffer.write(utf8.decode(response));
if (buffer.toString().contains("\r\n\r\n")) {
break;
}
}
return buffer.toString();
}
}
6 changes: 6 additions & 0 deletions lib/utilities/test_node_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Future<bool> _xmrHelper(
final data = nodeFormData;
final url = data.host!;
final port = data.port;
final username = data.login;
final password = data.password;

final uri = Uri.parse(url);

Expand All @@ -51,6 +53,8 @@ Future<bool> _xmrHelper(

final response = await testMoneroNodeConnection(
Uri.parse(uriString),
username,
password,
false,
proxyInfo: proxyInfo,
).timeout(Duration(seconds: proxyInfo != null ? 30 : 10));
Expand All @@ -67,6 +71,8 @@ Future<bool> _xmrHelper(
if (shouldAllowBadCert) {
final response = await testMoneroNodeConnection(
Uri.parse(uriString),
username,
password,
true,
proxyInfo: proxyInfo,
);
Expand Down
2 changes: 2 additions & 0 deletions scripts/app_config/templates/pubspec.template
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ dependencies:
cbor: ^6.3.3
cs_monero: 1.0.0-pre.1
cs_monero_flutter_libs: 1.0.0-pre.0
monero_rpc: ^2.0.0
digest_auth: ^1.0.1

dev_dependencies:
flutter_test:
Expand Down
Loading