Skip to content

Commit 2aa548e

Browse files
Merge pull request #1063 from Cyrix126/node_auth
handle authentication in test of monero nodes
2 parents b44cde3 + 4c7cb0c commit 2aa548e

File tree

3 files changed

+67
-54
lines changed

3 files changed

+67
-54
lines changed

lib/utilities/test_monero_node_connection.dart

Lines changed: 59 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import 'dart:convert';
1212
import 'dart:io';
1313

1414
import 'package:flutter/material.dart';
15+
import 'package:http/io_client.dart';
16+
import 'package:monero_rpc/monero_rpc.dart';
17+
import 'package:digest_auth/digest_auth.dart';
1518
import 'package:socks5_proxy/socks.dart';
1619
import 'package:tor_ffi_plugin/socks_socket.dart';
1720

@@ -27,11 +30,18 @@ class MoneroNodeConnectionResponse {
2730
final int? port;
2831
final bool success;
2932

30-
MoneroNodeConnectionResponse(this.cert, this.url, this.port, this.success);
33+
MoneroNodeConnectionResponse(
34+
this.cert,
35+
this.url,
36+
this.port,
37+
this.success,
38+
);
3139
}
3240

3341
Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
3442
Uri uri,
43+
String? username,
44+
String? password,
3545
bool allowBadX509Certificate, {
3646
required ({
3747
InternetAddress host,
@@ -59,36 +69,37 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
5969
await socket.connect();
6070
await socket.connectTo(uri.host, uri.port);
6171

62-
final body = jsonEncode({
63-
"jsonrpc": "2.0",
64-
"id": "0",
65-
"method": "get_info",
66-
});
67-
68-
final request = 'POST /json_rpc HTTP/1.1\r\n'
69-
'Host: ${uri.host}\r\n'
70-
'Content-Type: application/json\r\n'
71-
'Content-Length: ${body.length}\r\n'
72-
'\r\n'
73-
'$body';
74-
75-
socket.write(request);
76-
print("Request sent: $request");
77-
78-
final buffer = StringBuffer();
79-
await for (var response in socket.inputStream) {
80-
buffer.write(utf8.decode(response));
81-
if (buffer.toString().contains("\r\n\r\n")) {
82-
break;
72+
final rawRequest = DaemonRpc.rawRequestRpc(uri, 'get_info', {});
73+
var response = await socket.send(rawRequest);
74+
// check if we need authentication
75+
String? authenticateHeaderValue;
76+
for (final line in response.split('\r\n')) {
77+
if (line.contains('WWW-authenticate: ')) {
78+
// both the password and username needs to be
79+
if (username == null || password == null) {
80+
// node asking us for authentication, but we don't have any crendentials.
81+
return MoneroNodeConnectionResponse(null, null, null, false);
82+
}
83+
authenticateHeaderValue =
84+
line.replaceFirst('WWW-authenticate: ', '').trim();
8385
}
8486
}
85-
86-
final result = buffer.toString();
87-
print("Response received: $result");
87+
// header to authenticate was present, we need to remake the request with digest
88+
if (authenticateHeaderValue != null) {
89+
final digestAuth = DigestAuth(username!, password!);
90+
digestAuth.initFromAuthorizationHeader(authenticateHeaderValue);
91+
92+
// generate the Authorization header for the second request.
93+
final authHeader = digestAuth.getAuthString('POST', uri.path);
94+
final rawRequestAuthenticated =
95+
DaemonRpc.rawRequestRpc(uri, 'get_info', {}, authHeader);
96+
// resend with an authenticated request
97+
response = await socket.send(rawRequestAuthenticated);
98+
}
8899

89100
// Check if the response contains "results" and does not contain "error"
90101
final success =
91-
result.contains('"result":') && !result.contains('"error"');
102+
response.contains('"result":') && !response.contains('"error"');
92103

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

125136
return false;
126137
};
127-
128-
final request = await httpClient.postUrl(uri);
129-
130-
final body = utf8.encode(
131-
jsonEncode({
132-
"jsonrpc": "2.0",
133-
"id": "0",
134-
"method": "get_info",
135-
}),
136-
);
137-
138-
request.headers.add(
139-
'Content-Length',
140-
body.length.toString(),
141-
preserveHeaderCase: true,
142-
);
143-
request.headers.set(
144-
'Content-Type',
145-
'application/json',
146-
preserveHeaderCase: true,
138+
final daemonRpc = DaemonRpc(
139+
IOClient(httpClient),
140+
'$uri',
141+
username: username,
142+
password: password,
147143
);
144+
final result = await daemonRpc.call('get_info', {});
148145

149-
request.add(body);
150-
151-
final response = await request.close();
152-
final result = await response.transform(utf8.decoder).join();
153-
// print("HTTP Response: $result");
154-
155-
final success =
156-
result.contains('"result":') && !result.contains('"error"');
146+
final success = result.containsKey('status') && result['status'] == 'OK';
157147

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

211201
return result ?? false;
212202
}
203+
204+
extension on SOCKSSocket {
205+
/// write the raw request to the socket and return the response as String
206+
Future<String> send(String rawRequest) async {
207+
write(rawRequest);
208+
final buffer = StringBuffer();
209+
await for (final response in inputStream) {
210+
buffer.write(utf8.decode(response));
211+
if (buffer.toString().contains("\r\n\r\n")) {
212+
break;
213+
}
214+
}
215+
return buffer.toString();
216+
}
217+
}

lib/utilities/test_node_connection.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Future<bool> _xmrHelper(
3838
final data = nodeFormData;
3939
final url = data.host!;
4040
final port = data.port;
41+
final username = data.login;
42+
final password = data.password;
4143

4244
final uri = Uri.parse(url);
4345

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

5254
final response = await testMoneroNodeConnection(
5355
Uri.parse(uriString),
56+
username,
57+
password,
5458
false,
5559
proxyInfo: proxyInfo,
5660
).timeout(Duration(seconds: proxyInfo != null ? 30 : 10));
@@ -67,6 +71,8 @@ Future<bool> _xmrHelper(
6771
if (shouldAllowBadCert) {
6872
final response = await testMoneroNodeConnection(
6973
Uri.parse(uriString),
74+
username,
75+
password,
7076
true,
7177
proxyInfo: proxyInfo,
7278
);

scripts/app_config/templates/pubspec.template

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ dependencies:
204204
cbor: ^6.3.3
205205
cs_monero: 1.0.0-pre.1
206206
cs_monero_flutter_libs: 1.0.0-pre.0
207+
monero_rpc: ^2.0.0
208+
digest_auth: ^1.0.1
207209

208210
dev_dependencies:
209211
flutter_test:

0 commit comments

Comments
 (0)