3
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
core 1ecaaa60ff
Add DNS resolver support 2023-12-05 10:24:19 -05:00
John Maguire ec1af2974a
Fix encryption errors after restoring to a new phone (#143)
When a user restores to a new phone, their TPM will no longer be able to
decrypt the encrypted credentials.

We have code already in place to delete "invalid" sites, which cleans
these up by removing them.

However, when trying to save a new site, Android continues to try to use
the old keys which are no longer decryptable. So, when saving new
encrypted files, simply reset the crypto keys if we are unable to
encrypt.
2023-12-01 15:26:21 -05:00
John Maguire fbd2759d4f
Replace 'DN' with 'Managed Nebula' (#139) 2023-10-05 14:41:40 -04:00
20 changed files with 370 additions and 71 deletions

View File

@ -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)
}
} }

View File

@ -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()
} }
} }

View File

@ -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())

View File

@ -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,

View File

@ -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
} }

View File

@ -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) {

View File

@ -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

View File

@ -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'),

View File

@ -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
); );

View File

@ -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'));

View File

@ -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))
) )

View File

@ -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(

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}); });
}); });

View File

@ -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 {

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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"