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

feat(mobile): query DNS SVCB/HTTPS records #15029

Open
wants to merge 6 commits into
base: main
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
18 changes: 16 additions & 2 deletions mobile/lib/services/api.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:http/http.dart';

import 'package:dns_query_service.dart';

class ApiService implements Authentication {
late ApiClient _apiClient;

late UsersApi usersApi;
late AuthenticationApi authenticationApi;
late OAuthApi oAuthApi;
Expand All @@ -31,7 +32,8 @@ class ApiService implements Authentication {
late DownloadApi downloadApi;
late TrashApi trashApi;
late StacksApi stacksApi;

final DnsQueryService _dnsQueryService = DnsQueryService(); // Create DnsQueryService instance

ApiService() {
final endpoint = Store.tryGet(StoreKey.serverEndpoint);
if (endpoint != null && endpoint.isNotEmpty) {
Expand Down Expand Up @@ -82,13 +84,25 @@ class ApiService implements Authentication {
/// host - required
/// port - optional (default: based on schema)
/// path - optional
/// Resolve and set the API endpoint, with SRV record lookup based on domain
Future<String> resolveEndpoint(String serverUrl) async {
final url = sanitizeUrl(serverUrl);

if (!await _isEndpointAvailable(serverUrl)) {
throw ApiException(503, "Server is not reachable");
}

// Get domain name from serverUrl
final domain = Uri.parse(serverUrl).host;

// Call DnsQueryService to fetch SRV records
try {
List<Map<String, dynamic>> srvRecords = await _dnsQueryService.fetchSrvRecords(domain);
_log.info('Fetched SRV records for domain $domain: $srvRecords');
} catch (e) {
_log.severe('Failed to fetch SRV records for domain $domain: $e');
}

// Check for /.well-known/immich
final wellKnownEndpoint = await _getWellKnownEndpoint(url);
if (wellKnownEndpoint.isNotEmpty) return wellKnownEndpoint;
Expand Down
47 changes: 47 additions & 0 deletions mobile/lib/services/dns_query_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:async';
import 'package:basic_utils/basic_utils.dart';
import 'package:logging/logging.dart';

class DnsQueryService {
final Logger _log = Logger('DnsQueryService');

/// Validates the domain name format.
bool _isValidDomain(String domain) {
final regex = RegExp(r'^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$');
return regex.hasMatch(domain);
}

/// Fetch SRV records for a given domain.
Future<List<Map<String, dynamic>>> fetchSrvRecords(String domain) async {
if (!_isValidDomain(domain)) {
_log.warning('Invalid domain name: $domain');
throw Exception('Invalid domain name: $domain');
}

try {
_log.info('Attempting to fetch SRV records for domain: $domain');

// Perform the SRV record lookup
final srvRecords = await DnsUtils.lookupRecord(domain, RRecordType.SRV);

_log.info('Successfully fetched SRV records for domain: $domain');

// Transform the result into a list of maps
return srvRecords.map((record) {
return {
'priority': record.priority,
'weight': record.weight,
'port': record.port,
'target': record.target,
};
}).toList();
} catch (e, stackTrace) {
_log.severe(
'Failed to fetch SRV records for domain: $domain',
e,
stackTrace,
);
throw Exception('Failed to fetch SRV records: $e');
}
}
}
Loading