Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to add DNS resolvers to a site #104

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ class NebulaVpnService : VpnService() {
builder.addRoute(unsafeIPNet.network, unsafeIPNet.maskSize.toInt())
}

// Add our dns resolvers
site!!.dnsResolvers.forEach { dnsResolver ->
builder.addDnsServer(dnsResolver)
}

val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
cm.allNetworks.forEach { network ->
cm.getLinkProperties(network).dnsServers.forEach { builder.addDnsServer(it) }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cm.getLinkProperties(network)?.dnsServers?.forEach { builder.addDnsServer(it) }

Would not compile otherwise.

Copy link

@numinit numinit Dec 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come to think of it, lines 129 to 132 should be removed entirely, as they cause a DNS leak on Android when tested with https://mullvad.net/ (but no others, interestingly). Removing these lines fixes the DNS leak. They are also based on a false assumption that the device will not roam between networks.

}

try {
vpnInterface = builder.establish()
nebula = mobileNebula.MobileNebula.newNebula(site!!.config, site!!.getKey(this), site!!.logFile, vpnInterface!!.detachFd().toLong())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class Site(context: Context, siteDir: File) {
val id: String
val staticHostmap: HashMap<String, StaticHosts>
val unsafeRoutes: List<UnsafeRoute>
val dnsResolvers: List<String>
var cert: CertificateInfo? = null
var ca: Array<CertificateInfo>
val lhDuration: Int
Expand Down Expand Up @@ -236,6 +237,7 @@ class Site(context: Context, siteDir: File) {
id = incomingSite.id
staticHostmap = incomingSite.staticHostmap
unsafeRoutes = incomingSite.unsafeRoutes ?: ArrayList()
dnsResolvers = incomingSite.dnsResolvers ?: ArrayList()
lhDuration = incomingSite.lhDuration
port = incomingSite.port
mtu = incomingSite.mtu ?: 1300
Expand Down Expand Up @@ -340,6 +342,7 @@ class IncomingSite(
val id: String,
val staticHostmap: HashMap<String, StaticHosts>,
val unsafeRoutes: List<UnsafeRoute>?,
val dnsResolvers: List<String>?,
val cert: String,
val ca: String,
val lhDuration: Int,
Expand Down
5 changes: 5 additions & 0 deletions ios/NebulaNetworkExtension/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
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
if (error != nil) {
return completionHandler(error!)
Expand Down
4 changes: 4 additions & 0 deletions ios/NebulaNetworkExtension/Site.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class Site: Codable {
// Stored in proto
var staticHostmap: Dictionary<String, StaticHosts>
var unsafeRoutes: [UnsafeRoute]
var dnsResolvers: [String]
var cert: CertificateInfo?
var ca: [CertificateInfo]
var lhDuration: Int
Expand Down Expand Up @@ -193,6 +194,7 @@ class Site: Codable {
id = incoming.id
staticHostmap = incoming.staticHostmap
unsafeRoutes = incoming.unsafeRoutes ?? []
dnsResolvers = incoming.dnsResolvers ?? []

do {
let rawCert = incoming.cert
Expand Down Expand Up @@ -339,6 +341,7 @@ class Site: Codable {
case status
case logFile
case unsafeRoutes
case dnsResolvers
case logVerbosity
case errors
case mtu
Expand Down Expand Up @@ -392,6 +395,7 @@ struct IncomingSite: Codable {
var id: String
var staticHostmap: Dictionary<String, StaticHosts>
var unsafeRoutes: [UnsafeRoute]?
var dnsResolvers: [String]?
var cert: String
var ca: String
var lhDuration: Int
Expand Down
14 changes: 14 additions & 0 deletions lib/models/Site.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:mobile_nebula/models/HostInfo.dart';
import 'package:mobile_nebula/models/UnsafeRoute.dart';
import 'package:mobile_nebula/models/IPAndPort.dart';
import 'package:uuid/uuid.dart';
import 'Certificate.dart';
import 'StaticHosts.dart';
Expand All @@ -24,6 +25,7 @@ class Site {
// static_host_map
late Map<String, StaticHost> staticHostmap;
late List<UnsafeRoute> unsafeRoutes;
late List<String> dnsResolvers;

// pki fields
late List<CertificateInfo> ca;
Expand Down Expand Up @@ -69,6 +71,7 @@ class Site {
String logVerbosity = 'info',
List<String>? errors,
List<UnsafeRoute>? unsafeRoutes,
List<String>? dnsResolvers,
bool managed = false,
String? rawConfig,
DateTime? lastManagedUpdate,
Expand All @@ -89,6 +92,7 @@ class Site {
this.logVerbosity = logVerbosity;
this.errors = errors ?? [];
this.unsafeRoutes = unsafeRoutes ?? [];
this.dnsResolvers = dnsResolvers ?? [];
this.managed = managed;
this.rawConfig = rawConfig;
this.lastManagedUpdate = lastManagedUpdate;
Expand Down Expand Up @@ -128,6 +132,7 @@ class Site {
logVerbosity: decoded['logVerbosity'],
errors: decoded['errors'],
unsafeRoutes: decoded['unsafeRoutes'],
dnsResolvers: decoded['dnsResolvers'],
managed: decoded['managed'],
rawConfig: decoded['rawConfig'],
lastManagedUpdate: decoded['lastManagedUpdate'],
Expand All @@ -152,6 +157,7 @@ class Site {
this.logVerbosity = decoded['logVerbosity'];
this.errors = decoded['errors'];
this.unsafeRoutes = decoded['unsafeRoutes'];
this.dnsResolvers = decoded['dnsResolvers'];
this.managed = decoded['managed'];
this.rawConfig = decoded['rawConfig'];
this.lastManagedUpdate = decoded['lastManagedUpdate'];
Expand All @@ -170,6 +176,12 @@ class Site {
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<CertificateInfo> ca = [];
rawCA.forEach((val) {
Expand Down Expand Up @@ -204,6 +216,7 @@ class Site {
"logVerbosity": json['logVerbosity'],
"errors": errors,
"unsafeRoutes": unsafeRoutes,
"dnsResolvers": dnsResolvers,
"managed": json['managed'] ?? false,
"rawConfig": json['rawConfig'],
"lastManagedUpdate": json["lastManagedUpdate"] == null ?
Expand All @@ -221,6 +234,7 @@ class Site {
'id': id,
'staticHostmap': staticHostmap,
'unsafeRoutes': unsafeRoutes,
'dnsResolvers': dnsResolvers,
'ca': ca.map((cert) {
return cert.rawCert;
}).join('\n'),
Expand Down
23 changes: 23 additions & 0 deletions lib/screens/siteConfig/AdvancedScreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:mobile_nebula/components/config/ConfigSection.dart';
import 'package:mobile_nebula/models/Site.dart';
import 'package:mobile_nebula/models/UnsafeRoute.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/RenderedConfigScreen.dart';
import 'package:mobile_nebula/services/utils.dart';
Expand All @@ -28,6 +29,7 @@ class Advanced {
String verbosity;
List<UnsafeRoute> unsafeRoutes;
int mtu;
List<String> dnsResolvers;

Advanced({
required this.lhDuration,
Expand All @@ -36,6 +38,7 @@ class Advanced {
required this.verbosity,
required this.unsafeRoutes,
required this.mtu,
required this.dnsResolvers,
});
}

Expand Down Expand Up @@ -66,6 +69,7 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
verbosity: widget.site.logVerbosity,
unsafeRoutes: widget.site.unsafeRoutes,
mtu: widget.site.mtu,
dnsResolvers: widget.site.dnsResolvers,
);
super.initState();
}
Expand Down Expand Up @@ -190,6 +194,25 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
changed = true;
});
});
});
},
),
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.dnsResolvers,
onSave: (dnsResolvers) {
setState(() {
settings.dnsResolvers = dnsResolvers;
changed = true;
});
});
});
},
)
Expand Down
77 changes: 77 additions & 0 deletions lib/screens/siteConfig/DNSResolverScreen.dart
Original file line number Diff line number Diff line change
@@ -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';

class 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);
}
}
}
Loading