3
0
Fork 0

Add DNS resolver support

This commit is contained in:
core 2023-12-05 10:24:19 -05:00
parent ec1af2974a
commit 1ecaaa60ff
Signed by: core
GPG Key ID: FDBF740DADDCEECF
11 changed files with 244 additions and 1 deletions

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

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

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

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