forked from core/mobile_nebula
Compare commits
3 Commits
fix-relay-
...
master
Author | SHA1 | Date |
---|---|---|
core | 1ecaaa60ff | |
John Maguire | ec1af2974a | |
John Maguire | fbd2759d4f |
|
@ -1,22 +1,57 @@
|
||||||
package net.defined.mobile_nebula
|
package net.defined.mobile_nebula
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.security.crypto.EncryptedFile
|
import androidx.security.crypto.EncryptedFile
|
||||||
import androidx.security.crypto.MasterKeys
|
import androidx.security.crypto.MasterKeys
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
import java.security.KeyStore
|
||||||
|
|
||||||
class EncFile(private val context: Context) {
|
class EncFile(private val context: Context) {
|
||||||
|
companion object {
|
||||||
|
// Borrowed from androidx.security.crypto.MasterKeys
|
||||||
|
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||||
|
|
||||||
|
// Borrowed from androidx.security.crypto.EncryptedFile
|
||||||
|
private const val KEYSET_PREF_NAME = "__androidx_security_crypto_encrypted_file_pref__"
|
||||||
|
}
|
||||||
|
|
||||||
private val scheme = EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
|
private val scheme = EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
|
||||||
private val master: String = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
private val spec = MasterKeys.AES256_GCM_SPEC
|
||||||
|
private var master: String = MasterKeys.getOrCreate(spec)
|
||||||
|
|
||||||
fun openRead(file: File): BufferedReader {
|
fun openRead(file: File): BufferedReader {
|
||||||
val eFile = EncryptedFile.Builder(file, context, master, scheme).build()
|
// We may fail to decrypt the file, in which case we'll raise an exception.
|
||||||
return eFile.openFileInput().bufferedReader()
|
// Callers should handle this exception by deleting the invalid file.
|
||||||
|
return build(file).openFileInput().bufferedReader()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openWrite(file: File): BufferedWriter {
|
fun openWrite(file: File): BufferedWriter {
|
||||||
val eFile = EncryptedFile.Builder(file, context, master, scheme).build()
|
return try {
|
||||||
return eFile.openFileOutput().bufferedWriter()
|
build(file).openFileOutput().bufferedWriter()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// If we fail to open the file, it's likely because the master key no longer works.
|
||||||
|
// We'll try to reset the master key and try again.
|
||||||
|
resetMasterKey()
|
||||||
|
|
||||||
|
build(file).openFileOutput().bufferedWriter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun build(file: File): EncryptedFile {
|
||||||
|
return EncryptedFile.Builder(file, context, master, scheme).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetMasterKey() {
|
||||||
|
// Reset the master key
|
||||||
|
KeyStore.getInstance(ANDROID_KEYSTORE).apply {
|
||||||
|
load(null)
|
||||||
|
deleteEntry(master)
|
||||||
|
}
|
||||||
|
// And reset the shared preference containing the file encryption key
|
||||||
|
context.deleteSharedPreferences(KEYSET_PREF_NAME)
|
||||||
|
|
||||||
|
// Re-create the master key now so future calls don't fail
|
||||||
|
master = MasterKeys.getOrCreate(spec)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -84,6 +84,10 @@ class MainActivity: FlutterActivity() {
|
||||||
"active.setRemoteForTunnel" -> activeSetRemoteForTunnel(call, result)
|
"active.setRemoteForTunnel" -> activeSetRemoteForTunnel(call, result)
|
||||||
"active.closeTunnel" -> activeCloseTunnel(call, result)
|
"active.closeTunnel" -> activeCloseTunnel(call, result)
|
||||||
|
|
||||||
|
"debug.clearKeys" -> {
|
||||||
|
EncFile(context).resetMasterKey()
|
||||||
|
}
|
||||||
|
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,11 @@ class NebulaVpnService : VpnService() {
|
||||||
builder.addRoute(unsafeIPNet.network, unsafeIPNet.maskSize.toInt())
|
builder.addRoute(unsafeIPNet.network, unsafeIPNet.maskSize.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add our DNS resolvers
|
||||||
|
site!!.dnsResolvers.forEach { dnsResolver ->
|
||||||
|
builder.addDnsServer(dnsResolver)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
vpnInterface = builder.establish()
|
vpnInterface = builder.establish()
|
||||||
nebula = mobileNebula.MobileNebula.newNebula(site!!.config, site!!.getKey(this), site!!.logFile, vpnInterface!!.detachFd().toLong())
|
nebula = mobileNebula.MobileNebula.newNebula(site!!.config, site!!.getKey(this), site!!.logFile, vpnInterface!!.detachFd().toLong())
|
||||||
|
|
|
@ -201,6 +201,7 @@ class Site(context: Context, siteDir: File) {
|
||||||
val id: String
|
val id: String
|
||||||
val staticHostmap: HashMap<String, StaticHosts>
|
val staticHostmap: HashMap<String, StaticHosts>
|
||||||
val unsafeRoutes: List<UnsafeRoute>
|
val unsafeRoutes: List<UnsafeRoute>
|
||||||
|
val dnsResolvers: List<String>
|
||||||
var cert: CertificateInfo? = null
|
var cert: CertificateInfo? = null
|
||||||
var ca: Array<CertificateInfo>
|
var ca: Array<CertificateInfo>
|
||||||
val lhDuration: Int
|
val lhDuration: Int
|
||||||
|
@ -236,6 +237,7 @@ class Site(context: Context, siteDir: File) {
|
||||||
id = incomingSite.id
|
id = incomingSite.id
|
||||||
staticHostmap = incomingSite.staticHostmap
|
staticHostmap = incomingSite.staticHostmap
|
||||||
unsafeRoutes = incomingSite.unsafeRoutes ?: ArrayList()
|
unsafeRoutes = incomingSite.unsafeRoutes ?: ArrayList()
|
||||||
|
dnsResolvers = incomingSite.dnsResolvers ?: ArrayList()
|
||||||
lhDuration = incomingSite.lhDuration
|
lhDuration = incomingSite.lhDuration
|
||||||
port = incomingSite.port
|
port = incomingSite.port
|
||||||
mtu = incomingSite.mtu ?: 1300
|
mtu = incomingSite.mtu ?: 1300
|
||||||
|
@ -340,6 +342,7 @@ class IncomingSite(
|
||||||
val id: String,
|
val id: String,
|
||||||
val staticHostmap: HashMap<String, StaticHosts>,
|
val staticHostmap: HashMap<String, StaticHosts>,
|
||||||
val unsafeRoutes: List<UnsafeRoute>?,
|
val unsafeRoutes: List<UnsafeRoute>?,
|
||||||
|
val dnsResolvers: List<String>?,
|
||||||
val cert: String,
|
val cert: String,
|
||||||
val ca: String,
|
val ca: String,
|
||||||
val lhDuration: Int,
|
val lhDuration: Int,
|
||||||
|
|
|
@ -30,6 +30,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
|
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
|
||||||
tunnelNetworkSettings.mtu = _site.mtu as NSNumber
|
tunnelNetworkSettings.mtu = _site.mtu as NSNumber
|
||||||
|
|
||||||
|
if !_site.dnsResolvers.isEmpty {
|
||||||
|
let dnsSettings = NEDNSSettings(servers: _site.dnsResolvers)
|
||||||
|
tunnelNetworkSettings.dnsSettings = dnsSettings
|
||||||
|
}
|
||||||
|
|
||||||
self.setTunnelNetworkSettings(tunnelNetworkSettings, completionHandler: {(error:Error?) in
|
self.setTunnelNetworkSettings(tunnelNetworkSettings, completionHandler: {(error:Error?) in
|
||||||
if (error != nil) {
|
if (error != nil) {
|
||||||
|
|
|
@ -121,6 +121,7 @@ class Site: Codable {
|
||||||
// Stored in proto
|
// Stored in proto
|
||||||
var staticHostmap: Dictionary<String, StaticHosts>
|
var staticHostmap: Dictionary<String, StaticHosts>
|
||||||
var unsafeRoutes: [UnsafeRoute]
|
var unsafeRoutes: [UnsafeRoute]
|
||||||
|
var dnsResolvers: [String]
|
||||||
var cert: CertificateInfo?
|
var cert: CertificateInfo?
|
||||||
var ca: [CertificateInfo]
|
var ca: [CertificateInfo]
|
||||||
var lhDuration: Int
|
var lhDuration: Int
|
||||||
|
@ -194,6 +195,7 @@ class Site: Codable {
|
||||||
id = incoming.id
|
id = incoming.id
|
||||||
staticHostmap = incoming.staticHostmap
|
staticHostmap = incoming.staticHostmap
|
||||||
unsafeRoutes = incoming.unsafeRoutes ?? []
|
unsafeRoutes = incoming.unsafeRoutes ?? []
|
||||||
|
dnsResolvers = incoming.dnsResolvers ?? []
|
||||||
lhDuration = incoming.lhDuration
|
lhDuration = incoming.lhDuration
|
||||||
port = incoming.port
|
port = incoming.port
|
||||||
cipher = incoming.cipher
|
cipher = incoming.cipher
|
||||||
|
@ -340,6 +342,7 @@ class Site: Codable {
|
||||||
case status
|
case status
|
||||||
case logFile
|
case logFile
|
||||||
case unsafeRoutes
|
case unsafeRoutes
|
||||||
|
case dnsResolvers
|
||||||
case logVerbosity
|
case logVerbosity
|
||||||
case errors
|
case errors
|
||||||
case mtu
|
case mtu
|
||||||
|
@ -394,6 +397,7 @@ struct IncomingSite: Codable {
|
||||||
var id: String
|
var id: String
|
||||||
var staticHostmap: Dictionary<String, StaticHosts>
|
var staticHostmap: Dictionary<String, StaticHosts>
|
||||||
var unsafeRoutes: [UnsafeRoute]?
|
var unsafeRoutes: [UnsafeRoute]?
|
||||||
|
vat dnsResolvers: [String]?
|
||||||
var cert: String
|
var cert: String
|
||||||
var ca: String
|
var ca: String
|
||||||
var lhDuration: Int
|
var lhDuration: Int
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mobile_nebula/models/HostInfo.dart';
|
import 'package:mobile_nebula/models/HostInfo.dart';
|
||||||
import 'package:mobile_nebula/models/UnsafeRoute.dart';
|
import 'package:mobile_nebula/models/UnsafeRoute.dart';
|
||||||
|
import 'package:mobile_nebula/models/IPAndPort.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
import 'Certificate.dart';
|
import 'Certificate.dart';
|
||||||
import 'StaticHosts.dart';
|
import 'StaticHosts.dart';
|
||||||
|
@ -24,6 +25,7 @@ class Site {
|
||||||
// static_host_map
|
// static_host_map
|
||||||
late Map<String, StaticHost> staticHostmap;
|
late Map<String, StaticHost> staticHostmap;
|
||||||
late List<UnsafeRoute> unsafeRoutes;
|
late List<UnsafeRoute> unsafeRoutes;
|
||||||
|
late List<String> dnsResolvers;
|
||||||
|
|
||||||
// pki fields
|
// pki fields
|
||||||
late List<CertificateInfo> ca;
|
late List<CertificateInfo> ca;
|
||||||
|
@ -69,6 +71,7 @@ class Site {
|
||||||
String logVerbosity = 'info',
|
String logVerbosity = 'info',
|
||||||
List<String>? errors,
|
List<String>? errors,
|
||||||
List<UnsafeRoute>? unsafeRoutes,
|
List<UnsafeRoute>? unsafeRoutes,
|
||||||
|
List<String>? dnsResolvers,
|
||||||
bool managed = false,
|
bool managed = false,
|
||||||
String? rawConfig,
|
String? rawConfig,
|
||||||
DateTime? lastManagedUpdate,
|
DateTime? lastManagedUpdate,
|
||||||
|
@ -89,6 +92,7 @@ class Site {
|
||||||
this.logVerbosity = logVerbosity;
|
this.logVerbosity = logVerbosity;
|
||||||
this.errors = errors ?? [];
|
this.errors = errors ?? [];
|
||||||
this.unsafeRoutes = unsafeRoutes ?? [];
|
this.unsafeRoutes = unsafeRoutes ?? [];
|
||||||
|
this.dnsResolvers = dnsResolvers ?? [];
|
||||||
this.managed = managed;
|
this.managed = managed;
|
||||||
this.rawConfig = rawConfig;
|
this.rawConfig = rawConfig;
|
||||||
this.lastManagedUpdate = lastManagedUpdate;
|
this.lastManagedUpdate = lastManagedUpdate;
|
||||||
|
@ -128,6 +132,7 @@ class Site {
|
||||||
logVerbosity: decoded['logVerbosity'],
|
logVerbosity: decoded['logVerbosity'],
|
||||||
errors: decoded['errors'],
|
errors: decoded['errors'],
|
||||||
unsafeRoutes: decoded['unsafeRoutes'],
|
unsafeRoutes: decoded['unsafeRoutes'],
|
||||||
|
dnsResolvers: decoded['dnsResolvers'],
|
||||||
managed: decoded['managed'],
|
managed: decoded['managed'],
|
||||||
rawConfig: decoded['rawConfig'],
|
rawConfig: decoded['rawConfig'],
|
||||||
lastManagedUpdate: decoded['lastManagedUpdate'],
|
lastManagedUpdate: decoded['lastManagedUpdate'],
|
||||||
|
@ -152,6 +157,7 @@ class Site {
|
||||||
this.logVerbosity = decoded['logVerbosity'];
|
this.logVerbosity = decoded['logVerbosity'];
|
||||||
this.errors = decoded['errors'];
|
this.errors = decoded['errors'];
|
||||||
this.unsafeRoutes = decoded['unsafeRoutes'];
|
this.unsafeRoutes = decoded['unsafeRoutes'];
|
||||||
|
this.dnsResolvers = decoded['dnsResolvers'];
|
||||||
this.managed = decoded['managed'];
|
this.managed = decoded['managed'];
|
||||||
this.rawConfig = decoded['rawConfig'];
|
this.rawConfig = decoded['rawConfig'];
|
||||||
this.lastManagedUpdate = decoded['lastManagedUpdate'];
|
this.lastManagedUpdate = decoded['lastManagedUpdate'];
|
||||||
|
@ -170,6 +176,12 @@ class Site {
|
||||||
unsafeRoutes.add(UnsafeRoute.fromJson(val));
|
unsafeRoutes.add(UnsafeRoute.fromJson(val));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
List<dynamic> rawDNSResolvers = json['dnsResolvers'];
|
||||||
|
List<String> dnsResolvers = [];
|
||||||
|
rawDNSResolvers.forEach((val) {
|
||||||
|
dnsResolvers.add(val);
|
||||||
|
});
|
||||||
|
|
||||||
List<dynamic> rawCA = json['ca'];
|
List<dynamic> rawCA = json['ca'];
|
||||||
List<CertificateInfo> ca = [];
|
List<CertificateInfo> ca = [];
|
||||||
rawCA.forEach((val) {
|
rawCA.forEach((val) {
|
||||||
|
@ -204,6 +216,7 @@ class Site {
|
||||||
"logVerbosity": json['logVerbosity'],
|
"logVerbosity": json['logVerbosity'],
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
"unsafeRoutes": unsafeRoutes,
|
"unsafeRoutes": unsafeRoutes,
|
||||||
|
"dnsResolvers": dnsResolvers,
|
||||||
"managed": json['managed'] ?? false,
|
"managed": json['managed'] ?? false,
|
||||||
"rawConfig": json['rawConfig'],
|
"rawConfig": json['rawConfig'],
|
||||||
"lastManagedUpdate": json["lastManagedUpdate"] == null ?
|
"lastManagedUpdate": json["lastManagedUpdate"] == null ?
|
||||||
|
@ -221,6 +234,7 @@ class Site {
|
||||||
'id': id,
|
'id': id,
|
||||||
'staticHostmap': staticHostmap,
|
'staticHostmap': staticHostmap,
|
||||||
'unsafeRoutes': unsafeRoutes,
|
'unsafeRoutes': unsafeRoutes,
|
||||||
|
'dnsResolvers': dnsResolvers,
|
||||||
'ca': ca.map((cert) {
|
'ca': ca.map((cert) {
|
||||||
return cert.rawCert;
|
return cert.rawCert;
|
||||||
}).join('\n'),
|
}).join('\n'),
|
||||||
|
|
|
@ -156,7 +156,7 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
|
||||||
|
|
||||||
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
|
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
|
||||||
return SimplePage(
|
return SimplePage(
|
||||||
title: Text('Enroll with DN', style: TextStyle(fontWeight: FontWeight.bold)),
|
title: Text('Enroll with Managed Nebula', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)),
|
child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)),
|
||||||
alignment: alignment
|
alignment: alignment
|
||||||
);
|
);
|
||||||
|
|
|
@ -119,7 +119,7 @@ class _MainScreenState extends State<MainScreen> {
|
||||||
children: [
|
children: [
|
||||||
_debugSave(badDebugSave),
|
_debugSave(badDebugSave),
|
||||||
_debugSave(goodDebugSave),
|
_debugSave(goodDebugSave),
|
||||||
_debugDNEnroll(),
|
_debugClearKeys(),
|
||||||
],
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
);
|
);
|
||||||
|
@ -289,15 +289,16 @@ class _MainScreenState extends State<MainScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _debugDNEnroll() {
|
Widget _debugClearKeys() {
|
||||||
return CupertinoButton(
|
return CupertinoButton(
|
||||||
child: Text('DN Enroll'),
|
child: Text("Clear Keys"),
|
||||||
onPressed: () => Utils.openPage(context, (context) {
|
onPressed: () async {
|
||||||
return EnrollmentScreen(allowCodeEntry: true);
|
await platform.invokeMethod("debug.clearKeys", null);
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_loadSites() async {
|
_loadSites() async {
|
||||||
//TODO: This can throw, we need to show an error dialog
|
//TODO: This can throw, we need to show an error dialog
|
||||||
Map<String, dynamic> rawSites = jsonDecode(await platform.invokeMethod('listSites'));
|
Map<String, dynamic> rawSites = jsonDecode(await platform.invokeMethod('listSites'));
|
||||||
|
|
|
@ -89,7 +89,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
|
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
|
||||||
items.add(ConfigSection(children: [
|
items.add(ConfigSection(children: [
|
||||||
ConfigPageItem(
|
ConfigPageItem(
|
||||||
label: Text('Enroll with DN'),
|
label: Text('Enroll with Managed Nebula'),
|
||||||
labelWidth: 200,
|
labelWidth: 200,
|
||||||
onPressed: () => Utils.openPage(context, (context) => EnrollmentScreen(stream: widget.stream, allowCodeEntry: true))
|
onPressed: () => Utils.openPage(context, (context) => EnrollmentScreen(stream: widget.stream, allowCodeEntry: true))
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||||
import 'package:mobile_nebula/models/Site.dart';
|
import 'package:mobile_nebula/models/Site.dart';
|
||||||
import 'package:mobile_nebula/models/UnsafeRoute.dart';
|
import 'package:mobile_nebula/models/UnsafeRoute.dart';
|
||||||
import 'package:mobile_nebula/screens/siteConfig/CipherScreen.dart';
|
import 'package:mobile_nebula/screens/siteConfig/CipherScreen.dart';
|
||||||
|
import 'package:mobile_nebula/screens/siteConfig/DNSResolversScreen.dart';
|
||||||
import 'package:mobile_nebula/screens/siteConfig/LogVerbosityScreen.dart';
|
import 'package:mobile_nebula/screens/siteConfig/LogVerbosityScreen.dart';
|
||||||
import 'package:mobile_nebula/screens/siteConfig/RenderedConfigScreen.dart';
|
import 'package:mobile_nebula/screens/siteConfig/RenderedConfigScreen.dart';
|
||||||
import 'package:mobile_nebula/services/utils.dart';
|
import 'package:mobile_nebula/services/utils.dart';
|
||||||
|
@ -28,6 +29,7 @@ class Advanced {
|
||||||
String verbosity;
|
String verbosity;
|
||||||
List<UnsafeRoute> unsafeRoutes;
|
List<UnsafeRoute> unsafeRoutes;
|
||||||
int mtu;
|
int mtu;
|
||||||
|
List<String> dnsResolvers;
|
||||||
|
|
||||||
Advanced({
|
Advanced({
|
||||||
required this.lhDuration,
|
required this.lhDuration,
|
||||||
|
@ -36,6 +38,7 @@ class Advanced {
|
||||||
required this.verbosity,
|
required this.verbosity,
|
||||||
required this.unsafeRoutes,
|
required this.unsafeRoutes,
|
||||||
required this.mtu,
|
required this.mtu,
|
||||||
|
required this.dnsResolvers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +69,7 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
|
||||||
verbosity: widget.site.logVerbosity,
|
verbosity: widget.site.logVerbosity,
|
||||||
unsafeRoutes: widget.site.unsafeRoutes,
|
unsafeRoutes: widget.site.unsafeRoutes,
|
||||||
mtu: widget.site.mtu,
|
mtu: widget.site.mtu,
|
||||||
|
dnsResolvers: widget.site.dnsResolvers,
|
||||||
);
|
);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
@ -192,7 +196,26 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
|
ConfigPageItem(
|
||||||
|
label: Text('DNS Resolvers'),
|
||||||
|
labelWidth: 150,
|
||||||
|
content: Text(
|
||||||
|
Utils.itemCountFormat(settings.dnsResolvers.length),
|
||||||
|
textAlign: TextAlign.end),
|
||||||
|
onPressed: () {
|
||||||
|
Utils.openPage(context, (context) {
|
||||||
|
return DNSResolversScreen(
|
||||||
|
dnsResolvers: settings.dnsResolversmm
|
||||||
|
onSave: (dnsResolvers) {
|
||||||
|
setState(() {
|
||||||
|
settings.dnsResolvers = dnsResolvers;
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ConfigSection(
|
ConfigSection(
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:mobile_nebula/components/FormPage.dart';
|
||||||
|
import 'package:mobile_nebula/components/IPFormField.dart';
|
||||||
|
import 'package:mobile_nebula/components/config/ConfigItem.dart';
|
||||||
|
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||||
|
import 'package:mobile_nebula/services/utils.dart';
|
||||||
|
|
||||||
|
lass DNSResolverScreen extends StatefulWidget {
|
||||||
|
const DNSResolverScreen({Key? key, required this.dnsResolver, required this.onDelete, required this.onSave}) : super(key: key);
|
||||||
|
|
||||||
|
final String dnsResolver;
|
||||||
|
final ValueChanged<String> onSave;
|
||||||
|
final Function onDelete;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DNSResolverScreenState createState() => _DNSResolverScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DNSResolverScreenState extends State<DNSResolverScreen> {
|
||||||
|
late String dnsResolver;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
FocusNode dnsResolverFocus = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
dnsResolver = widget.dnsResolver;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FormPage(
|
||||||
|
title: widget.onDelete == null ? 'New DNS Resolver' : 'Edit DNS Resolver',
|
||||||
|
changed: changed,
|
||||||
|
onSave: _onSave,
|
||||||
|
child: Column(children: [
|
||||||
|
ConfigSection(children: <Widget>[
|
||||||
|
ConfigItem(
|
||||||
|
label: Text('Address'),
|
||||||
|
content: IPFormField(
|
||||||
|
initialValue: dnsResolver,
|
||||||
|
ipOnly: true,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
focusNode: dnsResolverFocus,
|
||||||
|
onSaved: (v) {
|
||||||
|
dnsResolver = v.toString();
|
||||||
|
})),
|
||||||
|
]),
|
||||||
|
widget.onDelete != null
|
||||||
|
? Padding(
|
||||||
|
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: PlatformElevatedButton(
|
||||||
|
child: Text('Delete'),
|
||||||
|
color: CupertinoColors.systemRed.resolveFrom(context),
|
||||||
|
onPressed: () => Utils.confirmDelete(context, 'Delete DNS Resolver?', () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
widget.onDelete();
|
||||||
|
}),
|
||||||
|
)))
|
||||||
|
: Container()
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSave() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (widget.onSave != null) {
|
||||||
|
widget.onSave(dnsResolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:mobile_nebula/components/FormPage.dart';
|
||||||
|
import 'package:mobile_nebula/components/config/ConfigButtonItem.dart';
|
||||||
|
import 'package:mobile_nebula/components/config/ConfigPageItem.dart';
|
||||||
|
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
||||||
|
import 'package:mobile_nebula/screens/siteConfig/DNSResolverScreen.dart';
|
||||||
|
import 'package:mobile_nebula/services/utils.dart';
|
||||||
|
|
||||||
|
class DNSResolversScreen extends StatefulWidget {
|
||||||
|
const DNSResolversScreen(
|
||||||
|
{Key? key, required this.dnsResolvers, required this.onSave})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final List<String> dnsResolvers;
|
||||||
|
final ValueChanged<List<String>> onSave;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DNSResolversScreenState createState() => _DNSResolversScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DNSResolversScreenState extends State<DNSResolversScreen> {
|
||||||
|
late List<String> dnsResolvers = [];
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.dnsResolvers.forEach((dnsResolver) {
|
||||||
|
dnsResolvers.add(dnsResolver);
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FormPage(
|
||||||
|
title: 'DNS Resolvers',
|
||||||
|
changed: changed,
|
||||||
|
onSave: _onSave,
|
||||||
|
child: ConfigSection(
|
||||||
|
children: _build(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSave() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (widget.onSave != null) {
|
||||||
|
widget.onSave(dnsResolvers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _build() {
|
||||||
|
List<Widget> items = [];
|
||||||
|
for (var i=0; i<dnsResolvers.length;i++) {
|
||||||
|
final dnsResolver = dnsResolvers[i];
|
||||||
|
items.add(ConfigPageItem(
|
||||||
|
label: Text("Resolver"),
|
||||||
|
content: Text(dnsResolver, textAlign: TextAlign.end),
|
||||||
|
onPressed: () {
|
||||||
|
Utils.openPage(context, (context) {
|
||||||
|
return DNSResolverScreen(
|
||||||
|
dnsResolver: dnsResolver,
|
||||||
|
onSave: (dnsResolver) {
|
||||||
|
setState(() {
|
||||||
|
changed = true;
|
||||||
|
dnsResolvers[i] = dnsResolver;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDelete: () {
|
||||||
|
setState(() {
|
||||||
|
changed = true;
|
||||||
|
dnsResolvers.removeAt(i);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add(ConfigButtonItem(
|
||||||
|
content: Text('Add a new DNS resolver'),
|
||||||
|
onPressed: () {
|
||||||
|
Utils.openPage(context, (context) {
|
||||||
|
return DNSResolverScreen(
|
||||||
|
dnsResolver: "",
|
||||||
|
onSave: (dnsResolver) {
|
||||||
|
setState(() {
|
||||||
|
changed = true;
|
||||||
|
});
|
||||||
|
dnsResolvers.add(dnsResolver);
|
||||||
|
},
|
||||||
|
onDelete: () {},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
|
@ -292,6 +292,7 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
|
||||||
site.port = settings.port;
|
site.port = settings.port;
|
||||||
site.logVerbosity = settings.verbosity;
|
site.logVerbosity = settings.verbosity;
|
||||||
site.unsafeRoutes = settings.unsafeRoutes;
|
site.unsafeRoutes = settings.unsafeRoutes;
|
||||||
|
site.dnsResolvers = settings.dnsResolvers;
|
||||||
site.mtu = settings.mtu;
|
site.mtu = settings.mtu;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -139,6 +139,7 @@ type configTun struct {
|
||||||
MTU *int `yaml:"mtu,omitempty"`
|
MTU *int `yaml:"mtu,omitempty"`
|
||||||
Routes []configRoute `yaml:"routes"`
|
Routes []configRoute `yaml:"routes"`
|
||||||
UnsafeRoutes []configUnsafeRoute `yaml:"unsafe_routes"`
|
UnsafeRoutes []configUnsafeRoute `yaml:"unsafe_routes"`
|
||||||
|
DNSResolvers []string `yaml:"dns_resolvers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type configRoute struct {
|
type configRoute struct {
|
||||||
|
|
|
@ -8,7 +8,7 @@ require (
|
||||||
github.com/DefinedNet/dnapi v0.0.0-20221117210952-6f56f055f991
|
github.com/DefinedNet/dnapi v0.0.0-20221117210952-6f56f055f991
|
||||||
github.com/sirupsen/logrus v1.9.2
|
github.com/sirupsen/logrus v1.9.2
|
||||||
github.com/slackhq/nebula v1.7.2
|
github.com/slackhq/nebula v1.7.2
|
||||||
golang.org/x/crypto v0.9.0
|
golang.org/x/crypto v0.16.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,11 +34,13 @@ require (
|
||||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
||||||
github.com/vishvananda/netlink v1.1.0 // indirect
|
github.com/vishvananda/netlink v1.1.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
golang.org/x/mod v0.10.0 // indirect
|
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/mod v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/term v0.8.0 // indirect
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.9.1 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
golang.org/x/term v0.15.0 // indirect
|
||||||
|
golang.org/x/tools v0.16.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
|
|
@ -148,12 +148,22 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
|
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||||
|
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||||
|
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||||
|
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da h1:gS9sVMAeHM+gVBmM9bTM6vUi/NHv58O3QzJ3vjjN84M=
|
||||||
|
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da/go.mod h1:IEceR0jfVklLJXrbUe90rfdAFAYDW0SQwKl4qvO1GBQ=
|
||||||
|
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg=
|
||||||
|
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -166,6 +176,10 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||||
|
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||||
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -174,6 +188,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||||
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -191,9 +207,17 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||||
|
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||||
|
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
@ -204,6 +228,10 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||||
|
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
||||||
|
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||||
|
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
||||||
|
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -60,7 +60,7 @@ func RenderConfig(configData string, key string) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "# DN-managed config\n" + string(yamlCfg), nil
|
return "# Managed Nebula Config (defined.net)\n" + string(yamlCfg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, build the config
|
// Otherwise, build the config
|
||||||
|
@ -116,6 +116,13 @@ func RenderConfig(configData string, key string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dnsResolvers, ok := d["dnsResolvers"].([]interface{}); ok {
|
||||||
|
cfg.Tun.DNSResolvers = make([]string, len(dnsResolvers))
|
||||||
|
for i, r := range dnsResolvers {
|
||||||
|
cfg.Tun.DNSResolvers[i] = r.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
finalConfig, err := yaml.Marshal(cfg)
|
finalConfig, err := yaml.Marshal(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
84
pubspec.lock
84
pubspec.lock
|
@ -42,21 +42,21 @@ packages:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3+2"
|
version: "0.3.3+4"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -70,21 +70,21 @@ packages:
|
||||||
name: ffi
|
name: ffi
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.2"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.4"
|
||||||
file_picker:
|
file_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.2"
|
version: "5.2.10"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -103,21 +103,21 @@ packages:
|
||||||
name: flutter_platform_widgets
|
name: flutter_platform_widgets
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.2.6"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.15"
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_svg
|
name: flutter_svg
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.5"
|
version: "1.1.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -169,7 +169,7 @@ packages:
|
||||||
name: mime
|
name: mime
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.4"
|
||||||
package_info:
|
package_info:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -204,77 +204,63 @@ packages:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.1.0"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.1.0"
|
||||||
path_provider_ios:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_foundation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.3.0"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.7"
|
version: "2.2.0"
|
||||||
path_provider_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_macos
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.4"
|
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.0"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.2.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: petitparser
|
name: petitparser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.1.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.5"
|
||||||
process:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: process
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "4.2.4"
|
|
||||||
pull_to_refresh:
|
pull_to_refresh:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -288,14 +274,14 @@ packages:
|
||||||
name: share_plus
|
name: share_plus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.4"
|
||||||
share_plus_platform_interface:
|
share_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: share_plus_platform_interface
|
name: share_plus_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.3.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -349,63 +335,63 @@ packages:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.2"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.6"
|
version: "6.1.11"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.13"
|
version: "6.0.38"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.13"
|
version: "6.1.4"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.5"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "3.0.6"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.3"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.13"
|
version: "2.0.18"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.7"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -426,14 +412,14 @@ packages:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.4"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "1.0.2"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -443,4 +429,4 @@ packages:
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.1 <3.0.0"
|
dart: ">=2.18.1 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.3.0"
|
||||||
|
|
Loading…
Reference in New Issue