2020-07-27 20:43:58 +00:00
|
|
|
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');
|
|
|
|
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
|
|
|
|
String name;
|
|
|
|
String id;
|
|
|
|
|
|
|
|
// static_host_map
|
|
|
|
Map<String, StaticHost> staticHostmap;
|
|
|
|
List<UnsafeRoute> unsafeRoutes;
|
|
|
|
|
|
|
|
// pki fields
|
|
|
|
List<CertificateInfo> ca;
|
|
|
|
CertificateInfo cert;
|
|
|
|
String key;
|
|
|
|
|
|
|
|
// lighthouse options
|
|
|
|
int lhDuration; // in seconds
|
|
|
|
|
|
|
|
// listen settings
|
|
|
|
int port;
|
|
|
|
int mtu;
|
|
|
|
|
|
|
|
String cipher;
|
|
|
|
int sortKey;
|
|
|
|
bool connected;
|
|
|
|
String status;
|
|
|
|
String logFile;
|
|
|
|
String logVerbosity;
|
|
|
|
|
|
|
|
// A list of errors encountered while loading the site
|
|
|
|
List<String> errors;
|
|
|
|
|
|
|
|
Site(
|
|
|
|
{this.name,
|
|
|
|
id,
|
|
|
|
staticHostmap,
|
|
|
|
ca,
|
|
|
|
this.cert,
|
|
|
|
this.lhDuration = 0,
|
|
|
|
this.port = 0,
|
|
|
|
this.cipher = "aes",
|
|
|
|
this.sortKey,
|
2020-08-07 16:38:16 +00:00
|
|
|
this.mtu = 1300,
|
2020-07-27 20:43:58 +00:00
|
|
|
this.connected,
|
|
|
|
this.status,
|
|
|
|
this.logFile,
|
2020-08-07 16:38:16 +00:00
|
|
|
this.logVerbosity = 'info',
|
2020-07-27 20:43:58 +00:00
|
|
|
errors,
|
|
|
|
unsafeRoutes})
|
|
|
|
: staticHostmap = staticHostmap ?? {},
|
|
|
|
unsafeRoutes = unsafeRoutes ?? [],
|
|
|
|
errors = errors ?? [],
|
|
|
|
ca = ca ?? [],
|
|
|
|
id = id ?? uuid.v4();
|
|
|
|
|
|
|
|
Site.fromJson(Map<String, dynamic> json) {
|
|
|
|
name = json['name'];
|
|
|
|
id = json['id'];
|
|
|
|
|
|
|
|
Map<String, dynamic> rawHostmap = json['staticHostmap'];
|
|
|
|
staticHostmap = {};
|
|
|
|
rawHostmap.forEach((key, val) {
|
|
|
|
staticHostmap[key] = StaticHost.fromJson(val);
|
|
|
|
});
|
|
|
|
|
|
|
|
List<dynamic> rawUnsafeRoutes = json['unsafeRoutes'];
|
|
|
|
unsafeRoutes = [];
|
|
|
|
if (rawUnsafeRoutes != null) {
|
|
|
|
rawUnsafeRoutes.forEach((val) {
|
|
|
|
unsafeRoutes.add(UnsafeRoute.fromJson(val));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
List<dynamic> rawCA = json['ca'];
|
|
|
|
ca = [];
|
|
|
|
rawCA.forEach((val) {
|
|
|
|
ca.add(CertificateInfo.fromJson(val));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (json['cert'] != null) {
|
|
|
|
cert = CertificateInfo.fromJson(json['cert']);
|
|
|
|
}
|
|
|
|
|
|
|
|
lhDuration = json['lhDuration'];
|
|
|
|
port = json['port'];
|
|
|
|
mtu = json['mtu'];
|
|
|
|
cipher = json['cipher'];
|
|
|
|
sortKey = json['sortKey'];
|
|
|
|
logFile = json['logFile'];
|
|
|
|
logVerbosity = json['logVerbosity'];
|
|
|
|
connected = json['connected'] ?? false;
|
|
|
|
status = json['status'] ?? "";
|
|
|
|
|
|
|
|
errors = [];
|
|
|
|
List<dynamic> rawErrors = json["errors"];
|
|
|
|
rawErrors.forEach((error) {
|
|
|
|
errors.add(error);
|
|
|
|
});
|
|
|
|
|
|
|
|
_updates = EventChannel('net.defined.nebula/$id');
|
|
|
|
_updates.receiveBroadcastStream().listen((d) {
|
|
|
|
try {
|
|
|
|
this.status = d['status'];
|
|
|
|
this.connected = d['connected'];
|
|
|
|
_change.add(null);
|
|
|
|
} catch (err) {
|
|
|
|
//TODO: handle the error
|
|
|
|
print(err);
|
|
|
|
}
|
|
|
|
}, onError: (err) {
|
|
|
|
var error = err as PlatformException;
|
|
|
|
this.status = error.details['status'];
|
|
|
|
this.connected = error.details['connected'];
|
|
|
|
_change.addError(error.message);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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': cert?.rawCert,
|
|
|
|
'key': key,
|
|
|
|
'lhDuration': lhDuration,
|
|
|
|
'port': port,
|
|
|
|
'mtu': mtu,
|
|
|
|
'cipher': cipher,
|
|
|
|
'sortKey': sortKey,
|
|
|
|
'logVerbosity': logVerbosity,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
//TODO: fix this message
|
|
|
|
throw err.details ?? 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});
|
2020-08-31 21:59:38 +00:00
|
|
|
if (ret == null) {
|
|
|
|
return [];
|
|
|
|
}
|
2020-07-27 20:43:58 +00:00
|
|
|
|
|
|
|
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});
|
2020-08-31 21:59:38 +00:00
|
|
|
if (ret == null) {
|
|
|
|
return [];
|
|
|
|
}
|
2020-07-27 20:43:58 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|