mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-01-18 19:27:05 +00:00
64d45f66c7
This updates flutter to 3.24.1, the latest stable version, and also updates our flutter dependencies to latest. It targets the latest android sdk, 34, which is required if we want to publish a new version to the Google Play store. I also needed to make a few adjustments to handle deprecations. The biggest change is that I needed to wrap the main widget in MaterialApp to avoid problems with AdaptiveSwitch in iOS.
385 lines
11 KiB
Dart
385 lines
11 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:flutter/services.dart';
|
|
import 'package:mobile_nebula/models/HostInfo.dart';
|
|
import 'package:mobile_nebula/models/UnsafeRoute.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
import 'Certificate.dart';
|
|
import 'StaticHosts.dart';
|
|
|
|
var uuid = Uuid();
|
|
|
|
class Site {
|
|
static const platform = MethodChannel('net.defined.mobileNebula/NebulaVpnService');
|
|
late EventChannel _updates;
|
|
|
|
/// Signals that something about this site has changed. onError is called with an error string if there was an error
|
|
StreamController _change = StreamController.broadcast();
|
|
|
|
// Identifiers
|
|
late String name;
|
|
late String id;
|
|
|
|
// static_host_map
|
|
late Map<String, StaticHost> staticHostmap;
|
|
late List<UnsafeRoute> unsafeRoutes;
|
|
|
|
// pki fields
|
|
late List<CertificateInfo> ca;
|
|
String? key;
|
|
late CertificateInfo? certInfo;
|
|
|
|
// lighthouse options
|
|
late int lhDuration; // in seconds
|
|
|
|
// listen settings
|
|
late int port;
|
|
late int mtu;
|
|
|
|
late String cipher;
|
|
late int sortKey;
|
|
late bool connected;
|
|
late String status;
|
|
late String logFile;
|
|
late String logVerbosity;
|
|
|
|
late bool managed;
|
|
// The following fields are present when managed = true
|
|
late String? rawConfig;
|
|
late DateTime? lastManagedUpdate;
|
|
|
|
// A list of errors encountered while loading the site
|
|
late List<String> errors;
|
|
|
|
Site({
|
|
String name = '',
|
|
String? id,
|
|
Map<String, StaticHost>? staticHostmap,
|
|
List<CertificateInfo>? ca,
|
|
CertificateInfo? certInfo,
|
|
int lhDuration = 0,
|
|
int port = 0,
|
|
String cipher = "aes",
|
|
int sortKey = 0,
|
|
int mtu = 1300,
|
|
bool connected = false,
|
|
String status = '',
|
|
String logFile = '',
|
|
String logVerbosity = 'info',
|
|
List<String>? errors,
|
|
List<UnsafeRoute>? unsafeRoutes,
|
|
bool managed = false,
|
|
String? rawConfig,
|
|
DateTime? lastManagedUpdate,
|
|
}) {
|
|
this.name = name;
|
|
this.id = id ?? uuid.v4();
|
|
this.staticHostmap = staticHostmap ?? {};
|
|
this.ca = ca ?? [];
|
|
this.certInfo = certInfo;
|
|
this.lhDuration = lhDuration;
|
|
this.port = port;
|
|
this.cipher = cipher;
|
|
this.sortKey = sortKey;
|
|
this.mtu = mtu;
|
|
this.connected = connected;
|
|
this.status = status;
|
|
this.logFile = logFile;
|
|
this.logVerbosity = logVerbosity;
|
|
this.errors = errors ?? [];
|
|
this.unsafeRoutes = unsafeRoutes ?? [];
|
|
this.managed = managed;
|
|
this.rawConfig = rawConfig;
|
|
this.lastManagedUpdate = lastManagedUpdate;
|
|
|
|
_updates = EventChannel('net.defined.nebula/$id');
|
|
_updates.receiveBroadcastStream().listen((d) {
|
|
try {
|
|
_updateFromJson(d);
|
|
_change.add(null);
|
|
} catch (err) {
|
|
//TODO: handle the error
|
|
print(err);
|
|
}
|
|
}, onError: (err) {
|
|
_updateFromJson(err.details);
|
|
var error = err as PlatformException;
|
|
_change.addError(error.message ?? 'An unexpected error occurred');
|
|
});
|
|
}
|
|
|
|
factory Site.fromJson(Map<String, dynamic> json) {
|
|
var decoded = Site._fromJson(json);
|
|
return Site(
|
|
name: decoded["name"],
|
|
id: decoded['id'],
|
|
staticHostmap: decoded['staticHostmap'],
|
|
ca: decoded['ca'],
|
|
certInfo: decoded['certInfo'],
|
|
lhDuration: decoded['lhDuration'],
|
|
port: decoded['port'],
|
|
cipher: decoded['cipher'],
|
|
sortKey: decoded['sortKey'],
|
|
mtu: decoded['mtu'],
|
|
connected: decoded['connected'],
|
|
status: decoded['status'],
|
|
logFile: decoded['logFile'],
|
|
logVerbosity: decoded['logVerbosity'],
|
|
errors: decoded['errors'],
|
|
unsafeRoutes: decoded['unsafeRoutes'],
|
|
managed: decoded['managed'],
|
|
rawConfig: decoded['rawConfig'],
|
|
lastManagedUpdate: decoded['lastManagedUpdate'],
|
|
);
|
|
}
|
|
|
|
_updateFromJson(String json) {
|
|
var decoded = Site._fromJson(jsonDecode(json));
|
|
this.name = decoded["name"];
|
|
this.id = decoded['id']; // TODO update EventChannel
|
|
this.staticHostmap = decoded['staticHostmap'];
|
|
this.ca = decoded['ca'];
|
|
this.certInfo = decoded['certInfo'];
|
|
this.lhDuration = decoded['lhDuration'];
|
|
this.port = decoded['port'];
|
|
this.cipher = decoded['cipher'];
|
|
this.sortKey = decoded['sortKey'];
|
|
this.mtu = decoded['mtu'];
|
|
this.connected = decoded['connected'];
|
|
this.status = decoded['status'];
|
|
this.logFile = decoded['logFile'];
|
|
this.logVerbosity = decoded['logVerbosity'];
|
|
this.errors = decoded['errors'];
|
|
this.unsafeRoutes = decoded['unsafeRoutes'];
|
|
this.managed = decoded['managed'];
|
|
this.rawConfig = decoded['rawConfig'];
|
|
this.lastManagedUpdate = decoded['lastManagedUpdate'];
|
|
}
|
|
|
|
static _fromJson(Map<String, dynamic> json) {
|
|
Map<String, dynamic> rawHostmap = json['staticHostmap'];
|
|
Map<String, StaticHost> staticHostmap = {};
|
|
rawHostmap.forEach((key, val) {
|
|
staticHostmap[key] = StaticHost.fromJson(val);
|
|
});
|
|
|
|
List<dynamic> rawUnsafeRoutes = json['unsafeRoutes'];
|
|
List<UnsafeRoute> unsafeRoutes = [];
|
|
rawUnsafeRoutes.forEach((val) {
|
|
unsafeRoutes.add(UnsafeRoute.fromJson(val));
|
|
});
|
|
|
|
List<dynamic> rawCA = json['ca'];
|
|
List<CertificateInfo> ca = [];
|
|
rawCA.forEach((val) {
|
|
ca.add(CertificateInfo.fromJson(val));
|
|
});
|
|
|
|
CertificateInfo? certInfo;
|
|
if (json['cert'] != null) {
|
|
certInfo = CertificateInfo.fromJson(json['cert']);
|
|
}
|
|
|
|
List<dynamic> rawErrors = json["errors"];
|
|
List<String> errors = [];
|
|
rawErrors.forEach((error) {
|
|
errors.add(error);
|
|
});
|
|
|
|
return {
|
|
"name": json["name"],
|
|
"id": json['id'],
|
|
"staticHostmap": staticHostmap,
|
|
"ca": ca,
|
|
"certInfo": certInfo,
|
|
"lhDuration": json['lhDuration'],
|
|
"port": json['port'],
|
|
"cipher": json['cipher'],
|
|
"sortKey": json['sortKey'],
|
|
"mtu": json['mtu'],
|
|
"connected": json['connected'] ?? false,
|
|
"status": json['status'] ?? "",
|
|
"logFile": json['logFile'],
|
|
"logVerbosity": json['logVerbosity'],
|
|
"errors": errors,
|
|
"unsafeRoutes": unsafeRoutes,
|
|
"managed": json['managed'] ?? false,
|
|
"rawConfig": json['rawConfig'],
|
|
"lastManagedUpdate": json["lastManagedUpdate"] == null ? null : DateTime.parse(json["lastManagedUpdate"]),
|
|
};
|
|
}
|
|
|
|
Stream onChange() {
|
|
return _change.stream;
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'name': name,
|
|
'id': id,
|
|
'staticHostmap': staticHostmap,
|
|
'unsafeRoutes': unsafeRoutes,
|
|
'ca': ca.map((cert) {
|
|
return cert.rawCert;
|
|
}).join('\n'),
|
|
'cert': certInfo?.rawCert,
|
|
'key': key,
|
|
'lhDuration': lhDuration,
|
|
'port': port,
|
|
'mtu': mtu,
|
|
'cipher': cipher,
|
|
'sortKey': sortKey,
|
|
'logVerbosity': logVerbosity,
|
|
'managed': managed,
|
|
'rawConfig': rawConfig,
|
|
};
|
|
}
|
|
|
|
save() async {
|
|
try {
|
|
var raw = jsonEncode(this);
|
|
await platform.invokeMethod("saveSite", raw);
|
|
} on PlatformException catch (err) {
|
|
//TODO: fix this message
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<String> renderConfig() async {
|
|
try {
|
|
var raw = jsonEncode(this);
|
|
return await platform.invokeMethod("nebula.renderConfig", raw);
|
|
} on PlatformException catch (err) {
|
|
//TODO: fix this message
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
start() async {
|
|
try {
|
|
await platform.invokeMethod("startSite", <String, String>{"id": id});
|
|
} on PlatformException catch (err) {
|
|
throw err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
stop() async {
|
|
try {
|
|
await platform.invokeMethod("stopSite", <String, String>{"id": id});
|
|
} on PlatformException catch (err) {
|
|
//TODO: fix this message
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<List<HostInfo>> listHostmap() async {
|
|
try {
|
|
var ret = await platform.invokeMethod("active.listHostmap", <String, String>{"id": id});
|
|
if (ret == null || ret == "null") {
|
|
return [];
|
|
}
|
|
|
|
List<dynamic> f = jsonDecode(ret);
|
|
List<HostInfo> hosts = [];
|
|
f.forEach((v) {
|
|
hosts.add(HostInfo.fromJson(v));
|
|
});
|
|
|
|
return hosts;
|
|
} on PlatformException catch (err) {
|
|
//TODO: fix this message
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<List<HostInfo>> listPendingHostmap() async {
|
|
try {
|
|
var ret = await platform.invokeMethod("active.listPendingHostmap", <String, String>{"id": id});
|
|
if (ret == null || ret == "null") {
|
|
return [];
|
|
}
|
|
|
|
List<dynamic> f = jsonDecode(ret);
|
|
List<HostInfo> hosts = [];
|
|
f.forEach((v) {
|
|
hosts.add(HostInfo.fromJson(v));
|
|
});
|
|
|
|
return hosts;
|
|
} on PlatformException catch (err) {
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<Map<String, List<HostInfo>>> listAllHostmaps() async {
|
|
try {
|
|
var res = await Future.wait([this.listHostmap(), this.listPendingHostmap()]);
|
|
return {"active": res[0], "pending": res[1]};
|
|
} on PlatformException catch (err) {
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
_change.close();
|
|
}
|
|
|
|
Future<HostInfo?> getHostInfo(String vpnIp, bool pending) async {
|
|
try {
|
|
var ret = await platform
|
|
.invokeMethod("active.getHostInfo", <String, dynamic>{"id": id, "vpnIp": vpnIp, "pending": pending});
|
|
final h = jsonDecode(ret);
|
|
if (h == null) {
|
|
return null;
|
|
}
|
|
|
|
return HostInfo.fromJson(h);
|
|
} on PlatformException catch (err) {
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<HostInfo?> setRemoteForTunnel(String vpnIp, String addr) async {
|
|
try {
|
|
var ret = await platform
|
|
.invokeMethod("active.setRemoteForTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp, "addr": addr});
|
|
final h = jsonDecode(ret);
|
|
if (h == null) {
|
|
return null;
|
|
}
|
|
|
|
return HostInfo.fromJson(h);
|
|
} on PlatformException catch (err) {
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
|
|
Future<bool> closeTunnel(String vpnIp) async {
|
|
try {
|
|
return await platform.invokeMethod("active.closeTunnel", <String, dynamic>{"id": id, "vpnIp": vpnIp});
|
|
} on PlatformException catch (err) {
|
|
throw err.details ?? err.message ?? err.toString();
|
|
} catch (err) {
|
|
throw err.toString();
|
|
}
|
|
}
|
|
}
|