viewmodel: port to kotlin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
55849cad65
commit
902a1ce46e
@ -1,5 +1,6 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
version wireguardVersionName
|
version wireguardVersionName
|
||||||
group groupName
|
group groupName
|
||||||
|
@ -83,7 +83,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
|||||||
|
|
||||||
override fun onExcludedAppsSelected(excludedApps: List<String>) {
|
override fun onExcludedAppsSelected(excludedApps: List<String>) {
|
||||||
requireNotNull(binding) { "Tried to set excluded apps while no view was loaded" }
|
requireNotNull(binding) { "Tried to set excluded apps while no view was loaded" }
|
||||||
binding!!.config!!.getInterface().excludedApplications.apply {
|
binding!!.config!!.`interface`.excludedApplications.apply {
|
||||||
clear()
|
clear()
|
||||||
addAll(excludedApps)
|
addAll(excludedApps)
|
||||||
}
|
}
|
||||||
@ -125,12 +125,12 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
|||||||
tunnel == null -> {
|
tunnel == null -> {
|
||||||
Log.d(TAG, "Attempting to create new tunnel " + binding!!.name)
|
Log.d(TAG, "Attempting to create new tunnel " + binding!!.name)
|
||||||
val manager = Application.getTunnelManager()
|
val manager = Application.getTunnelManager()
|
||||||
manager.create(binding!!.name, newConfig)
|
manager.create(binding!!.name!!, newConfig)
|
||||||
.whenComplete(this::onTunnelCreated)
|
.whenComplete(this::onTunnelCreated)
|
||||||
}
|
}
|
||||||
tunnel!!.name != binding!!.name -> {
|
tunnel!!.name != binding!!.name -> {
|
||||||
Log.d(TAG, "Attempting to rename tunnel to " + binding!!.name)
|
Log.d(TAG, "Attempting to rename tunnel to " + binding!!.name)
|
||||||
tunnel!!.setName(binding!!.name)
|
tunnel!!.setName(binding!!.name!!)
|
||||||
.whenComplete { _, t -> onTunnelRenamed(tunnel!!, newConfig, t) }
|
.whenComplete { _, t -> onTunnelRenamed(tunnel!!, newConfig, t) }
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -147,7 +147,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
|||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun onRequestSetExcludedApplications(view: View?) {
|
fun onRequestSetExcludedApplications(view: View?) {
|
||||||
if (binding != null) {
|
if (binding != null) {
|
||||||
val excludedApps = ArrayList(binding!!.config!!.getInterface().excludedApplications)
|
val excludedApps = ArrayList(binding!!.config!!.`interface`.excludedApplications)
|
||||||
val fragment = AppListDialogFragment.newInstance(excludedApps, this)
|
val fragment = AppListDialogFragment.newInstance(excludedApps, this)
|
||||||
fragment.show(parentFragmentManager, null)
|
fragment.show(parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wireguard.android.viewmodel;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import com.wireguard.config.BadConfigException;
|
|
||||||
import com.wireguard.config.Config;
|
|
||||||
import com.wireguard.config.Peer;
|
|
||||||
import com.wireguard.util.NonNullForAll;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import androidx.databinding.ObservableArrayList;
|
|
||||||
import androidx.databinding.ObservableList;
|
|
||||||
|
|
||||||
@NonNullForAll
|
|
||||||
public class ConfigProxy implements Parcelable {
|
|
||||||
public static final Parcelable.Creator<ConfigProxy> CREATOR = new ConfigProxyCreator();
|
|
||||||
|
|
||||||
private final InterfaceProxy interfaze;
|
|
||||||
private final ObservableList<PeerProxy> peers = new ObservableArrayList<>();
|
|
||||||
|
|
||||||
private ConfigProxy(final Parcel in) {
|
|
||||||
interfaze = in.readParcelable(InterfaceProxy.class.getClassLoader());
|
|
||||||
in.readTypedList(peers, PeerProxy.CREATOR);
|
|
||||||
for (final PeerProxy proxy : peers)
|
|
||||||
proxy.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigProxy(final Config other) {
|
|
||||||
interfaze = new InterfaceProxy(other.getInterface());
|
|
||||||
for (final Peer peer : other.getPeers()) {
|
|
||||||
final PeerProxy proxy = new PeerProxy(peer);
|
|
||||||
peers.add(proxy);
|
|
||||||
proxy.bind(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigProxy() {
|
|
||||||
interfaze = new InterfaceProxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerProxy addPeer() {
|
|
||||||
final PeerProxy proxy = new PeerProxy();
|
|
||||||
peers.add(proxy);
|
|
||||||
proxy.bind(this);
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InterfaceProxy getInterface() {
|
|
||||||
return interfaze;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableList<PeerProxy> getPeers() {
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config resolve() throws BadConfigException {
|
|
||||||
final Collection<Peer> resolvedPeers = new ArrayList<>();
|
|
||||||
for (final PeerProxy proxy : peers)
|
|
||||||
resolvedPeers.add(proxy.resolve());
|
|
||||||
return new Config.Builder()
|
|
||||||
.setInterface(interfaze.resolve())
|
|
||||||
.addPeers(resolvedPeers)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
|
||||||
dest.writeParcelable(interfaze, flags);
|
|
||||||
dest.writeTypedList(peers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConfigProxyCreator implements Parcelable.Creator<ConfigProxy> {
|
|
||||||
@Override
|
|
||||||
public ConfigProxy createFromParcel(final Parcel in) {
|
|
||||||
return new ConfigProxy(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigProxy[] newArray(final int size) {
|
|
||||||
return new ConfigProxy[size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package com.wireguard.android.viewmodel
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.databinding.ObservableArrayList
|
||||||
|
import androidx.databinding.ObservableList
|
||||||
|
import com.wireguard.config.BadConfigException
|
||||||
|
import com.wireguard.config.Config
|
||||||
|
import com.wireguard.config.Peer
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
class ConfigProxy : Parcelable {
|
||||||
|
val `interface`: InterfaceProxy
|
||||||
|
val peers: ObservableList<PeerProxy> = ObservableArrayList()
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) {
|
||||||
|
`interface` = parcel.readParcelable(InterfaceProxy::class.java.classLoader)!!
|
||||||
|
parcel.readTypedList(peers, PeerProxy.CREATOR)
|
||||||
|
peers.forEach { it.bind(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(other: Config) {
|
||||||
|
`interface` = InterfaceProxy(other.getInterface())
|
||||||
|
other.peers.forEach {
|
||||||
|
val proxy = PeerProxy(it)
|
||||||
|
peers.add(proxy)
|
||||||
|
proxy.bind(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
`interface` = InterfaceProxy()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addPeer(): PeerProxy {
|
||||||
|
val proxy = PeerProxy()
|
||||||
|
peers.add(proxy)
|
||||||
|
proxy.bind(this)
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents() = 0
|
||||||
|
|
||||||
|
@Throws(BadConfigException::class)
|
||||||
|
fun resolve(): Config {
|
||||||
|
val resolvedPeers: MutableCollection<Peer> = ArrayList()
|
||||||
|
peers.forEach { resolvedPeers.add(it.resolve()) }
|
||||||
|
return Config.Builder()
|
||||||
|
.setInterface(`interface`.resolve())
|
||||||
|
.addPeers(resolvedPeers)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
dest.writeParcelable(`interface`, flags)
|
||||||
|
dest.writeTypedList(peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConfigProxyCreator : Parcelable.Creator<ConfigProxy> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): ConfigProxy {
|
||||||
|
return ConfigProxy(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<ConfigProxy?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val CREATOR: Parcelable.Creator<ConfigProxy> = ConfigProxyCreator()
|
||||||
|
}
|
||||||
|
}
|
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wireguard.android.viewmodel;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import com.wireguard.android.BR;
|
|
||||||
import com.wireguard.config.Attribute;
|
|
||||||
import com.wireguard.config.BadConfigException;
|
|
||||||
import com.wireguard.config.Interface;
|
|
||||||
import com.wireguard.crypto.Key;
|
|
||||||
import com.wireguard.crypto.KeyFormatException;
|
|
||||||
import com.wireguard.crypto.KeyPair;
|
|
||||||
import com.wireguard.util.NonNullForAll;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.databinding.BaseObservable;
|
|
||||||
import androidx.databinding.Bindable;
|
|
||||||
import androidx.databinding.ObservableArrayList;
|
|
||||||
import androidx.databinding.ObservableList;
|
|
||||||
import java9.util.stream.Collectors;
|
|
||||||
import java9.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
@NonNullForAll
|
|
||||||
public class InterfaceProxy extends BaseObservable implements Parcelable {
|
|
||||||
public static final Parcelable.Creator<InterfaceProxy> CREATOR = new InterfaceProxyCreator();
|
|
||||||
|
|
||||||
private final ObservableList<String> excludedApplications = new ObservableArrayList<>();
|
|
||||||
private String addresses;
|
|
||||||
private String dnsServers;
|
|
||||||
private String listenPort;
|
|
||||||
private String mtu;
|
|
||||||
private String privateKey;
|
|
||||||
private String publicKey;
|
|
||||||
|
|
||||||
private InterfaceProxy(final Parcel in) {
|
|
||||||
addresses = in.readString();
|
|
||||||
dnsServers = in.readString();
|
|
||||||
in.readStringList(excludedApplications);
|
|
||||||
listenPort = in.readString();
|
|
||||||
mtu = in.readString();
|
|
||||||
privateKey = in.readString();
|
|
||||||
publicKey = in.readString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InterfaceProxy(final Interface other) {
|
|
||||||
addresses = Attribute.join(other.getAddresses());
|
|
||||||
final List<String> dnsServerStrings = StreamSupport.stream(other.getDnsServers())
|
|
||||||
.map(InetAddress::getHostAddress)
|
|
||||||
.collect(Collectors.toUnmodifiableList());
|
|
||||||
dnsServers = Attribute.join(dnsServerStrings);
|
|
||||||
excludedApplications.addAll(other.getExcludedApplications());
|
|
||||||
listenPort = other.getListenPort().map(String::valueOf).orElse("");
|
|
||||||
mtu = other.getMtu().map(String::valueOf).orElse("");
|
|
||||||
final KeyPair keyPair = other.getKeyPair();
|
|
||||||
privateKey = keyPair.getPrivateKey().toBase64();
|
|
||||||
publicKey = keyPair.getPublicKey().toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InterfaceProxy() {
|
|
||||||
addresses = "";
|
|
||||||
dnsServers = "";
|
|
||||||
listenPort = "";
|
|
||||||
mtu = "";
|
|
||||||
privateKey = "";
|
|
||||||
publicKey = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateKeyPair() {
|
|
||||||
final KeyPair keyPair = new KeyPair();
|
|
||||||
privateKey = keyPair.getPrivateKey().toBase64();
|
|
||||||
publicKey = keyPair.getPublicKey().toBase64();
|
|
||||||
notifyPropertyChanged(BR.privateKey);
|
|
||||||
notifyPropertyChanged(BR.publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getAddresses() {
|
|
||||||
return addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getDnsServers() {
|
|
||||||
return dnsServers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableList<String> getExcludedApplications() {
|
|
||||||
return excludedApplications;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getListenPort() {
|
|
||||||
return listenPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getMtu() {
|
|
||||||
return mtu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getPrivateKey() {
|
|
||||||
return privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Interface resolve() throws BadConfigException {
|
|
||||||
final Interface.Builder builder = new Interface.Builder();
|
|
||||||
if (!addresses.isEmpty())
|
|
||||||
builder.parseAddresses(addresses);
|
|
||||||
if (!dnsServers.isEmpty())
|
|
||||||
builder.parseDnsServers(dnsServers);
|
|
||||||
if (!excludedApplications.isEmpty())
|
|
||||||
builder.excludeApplications(excludedApplications);
|
|
||||||
if (!listenPort.isEmpty())
|
|
||||||
builder.parseListenPort(listenPort);
|
|
||||||
if (!mtu.isEmpty())
|
|
||||||
builder.parseMtu(mtu);
|
|
||||||
if (!privateKey.isEmpty())
|
|
||||||
builder.parsePrivateKey(privateKey);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAddresses(final String addresses) {
|
|
||||||
this.addresses = addresses;
|
|
||||||
notifyPropertyChanged(BR.addresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDnsServers(final String dnsServers) {
|
|
||||||
this.dnsServers = dnsServers;
|
|
||||||
notifyPropertyChanged(BR.dnsServers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListenPort(final String listenPort) {
|
|
||||||
this.listenPort = listenPort;
|
|
||||||
notifyPropertyChanged(BR.listenPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMtu(final String mtu) {
|
|
||||||
this.mtu = mtu;
|
|
||||||
notifyPropertyChanged(BR.mtu);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrivateKey(final String privateKey) {
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
try {
|
|
||||||
publicKey = new KeyPair(Key.fromBase64(privateKey)).getPublicKey().toBase64();
|
|
||||||
} catch (final KeyFormatException ignored) {
|
|
||||||
publicKey = "";
|
|
||||||
}
|
|
||||||
notifyPropertyChanged(BR.privateKey);
|
|
||||||
notifyPropertyChanged(BR.publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
|
||||||
dest.writeString(addresses);
|
|
||||||
dest.writeString(dnsServers);
|
|
||||||
dest.writeStringList(excludedApplications);
|
|
||||||
dest.writeString(listenPort);
|
|
||||||
dest.writeString(mtu);
|
|
||||||
dest.writeString(privateKey);
|
|
||||||
dest.writeString(publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class InterfaceProxyCreator implements Parcelable.Creator<InterfaceProxy> {
|
|
||||||
@Override
|
|
||||||
public InterfaceProxy createFromParcel(final Parcel in) {
|
|
||||||
return new InterfaceProxy(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InterfaceProxy[] newArray(final int size) {
|
|
||||||
return new InterfaceProxy[size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package com.wireguard.android.viewmodel
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.databinding.BaseObservable
|
||||||
|
import androidx.databinding.Bindable
|
||||||
|
import androidx.databinding.ObservableArrayList
|
||||||
|
import androidx.databinding.ObservableList
|
||||||
|
import com.wireguard.android.BR
|
||||||
|
import com.wireguard.config.Attribute
|
||||||
|
import com.wireguard.config.BadConfigException
|
||||||
|
import com.wireguard.config.Interface
|
||||||
|
import com.wireguard.crypto.Key
|
||||||
|
import com.wireguard.crypto.KeyFormatException
|
||||||
|
import com.wireguard.crypto.KeyPair
|
||||||
|
|
||||||
|
class InterfaceProxy : BaseObservable, Parcelable {
|
||||||
|
@get:Bindable
|
||||||
|
val excludedApplications: ObservableList<String> = ObservableArrayList()
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var addresses: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var dnsServers: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.dnsServers)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var listenPort: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.listenPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var mtu: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.mtu)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var privateKey: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.privateKey)
|
||||||
|
notifyPropertyChanged(BR.publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
val publicKey: String
|
||||||
|
get() = try {
|
||||||
|
KeyPair(Key.fromBase64(privateKey)).publicKey.toBase64()
|
||||||
|
} catch (ignored: KeyFormatException) {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) {
|
||||||
|
addresses = parcel.readString() ?: ""
|
||||||
|
dnsServers = parcel.readString() ?: ""
|
||||||
|
parcel.readStringList(excludedApplications)
|
||||||
|
listenPort = parcel.readString() ?: ""
|
||||||
|
mtu = parcel.readString() ?: ""
|
||||||
|
privateKey = parcel.readString() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(other: Interface) {
|
||||||
|
addresses = Attribute.join(other.addresses)
|
||||||
|
val dnsServerStrings = other.dnsServers.map { it.hostAddress }
|
||||||
|
dnsServers = Attribute.join(dnsServerStrings)
|
||||||
|
excludedApplications.addAll(other.excludedApplications)
|
||||||
|
listenPort = other.listenPort.map { it.toString() }.orElse("")
|
||||||
|
mtu = other.mtu.map { it.toString() }.orElse("")
|
||||||
|
val keyPair = other.keyPair
|
||||||
|
privateKey = keyPair.privateKey.toBase64()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
override fun describeContents() = 0
|
||||||
|
|
||||||
|
fun generateKeyPair() {
|
||||||
|
val keyPair = KeyPair()
|
||||||
|
privateKey = keyPair.privateKey.toBase64()
|
||||||
|
notifyPropertyChanged(BR.privateKey)
|
||||||
|
notifyPropertyChanged(BR.publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(BadConfigException::class)
|
||||||
|
fun resolve(): Interface {
|
||||||
|
val builder = Interface.Builder()
|
||||||
|
if (addresses.isNotEmpty()) builder.parseAddresses(addresses)
|
||||||
|
if (dnsServers.isNotEmpty()) builder.parseDnsServers(dnsServers)
|
||||||
|
if (excludedApplications.isNotEmpty()) builder.excludeApplications(excludedApplications)
|
||||||
|
if (listenPort.isNotEmpty()) builder.parseListenPort(listenPort)
|
||||||
|
if (mtu.isNotEmpty()) builder.parseMtu(mtu)
|
||||||
|
if (privateKey.isNotEmpty()) builder.parsePrivateKey(privateKey)
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
dest.writeString(addresses)
|
||||||
|
dest.writeString(dnsServers)
|
||||||
|
dest.writeStringList(excludedApplications)
|
||||||
|
dest.writeString(listenPort)
|
||||||
|
dest.writeString(mtu)
|
||||||
|
dest.writeString(privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InterfaceProxyCreator : Parcelable.Creator<InterfaceProxy> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): InterfaceProxy {
|
||||||
|
return InterfaceProxy(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<InterfaceProxy?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val CREATOR: Parcelable.Creator<InterfaceProxy> = InterfaceProxyCreator()
|
||||||
|
}
|
||||||
|
}
|
@ -1,382 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.wireguard.android.viewmodel;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
|
|
||||||
import com.wireguard.android.BR;
|
|
||||||
import com.wireguard.config.Attribute;
|
|
||||||
import com.wireguard.config.BadConfigException;
|
|
||||||
import com.wireguard.config.InetEndpoint;
|
|
||||||
import com.wireguard.config.Peer;
|
|
||||||
import com.wireguard.crypto.Key;
|
|
||||||
import com.wireguard.util.NonNullForAll;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.databinding.BaseObservable;
|
|
||||||
import androidx.databinding.Bindable;
|
|
||||||
import androidx.databinding.Observable;
|
|
||||||
import androidx.databinding.ObservableList;
|
|
||||||
import java9.util.Lists;
|
|
||||||
import java9.util.Sets;
|
|
||||||
import java9.util.stream.Collectors;
|
|
||||||
import java9.util.stream.Stream;
|
|
||||||
|
|
||||||
@NonNullForAll
|
|
||||||
public class PeerProxy extends BaseObservable implements Parcelable {
|
|
||||||
public static final Parcelable.Creator<PeerProxy> CREATOR = new PeerProxyCreator();
|
|
||||||
private static final Set<String> IPV4_PUBLIC_NETWORKS = new LinkedHashSet<>(Lists.of(
|
|
||||||
"0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", "32.0.0.0/3",
|
|
||||||
"64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", "172.0.0.0/12",
|
|
||||||
"172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", "173.0.0.0/8", "174.0.0.0/7",
|
|
||||||
"176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", "192.160.0.0/13", "192.169.0.0/16",
|
|
||||||
"192.170.0.0/15", "192.172.0.0/14", "192.176.0.0/12", "192.192.0.0/10",
|
|
||||||
"193.0.0.0/8", "194.0.0.0/7", "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4"
|
|
||||||
));
|
|
||||||
private static final Set<String> IPV4_WILDCARD = Sets.of("0.0.0.0/0");
|
|
||||||
|
|
||||||
private final List<String> dnsRoutes = new ArrayList<>();
|
|
||||||
private String allowedIps;
|
|
||||||
private AllowedIpsState allowedIpsState = AllowedIpsState.INVALID;
|
|
||||||
private String endpoint;
|
|
||||||
@Nullable private InterfaceDnsListener interfaceDnsListener;
|
|
||||||
@Nullable private ConfigProxy owner;
|
|
||||||
@Nullable private PeerListListener peerListListener;
|
|
||||||
private String persistentKeepalive;
|
|
||||||
private String preSharedKey;
|
|
||||||
private String publicKey;
|
|
||||||
private int totalPeers;
|
|
||||||
|
|
||||||
private PeerProxy(final Parcel in) {
|
|
||||||
allowedIps = in.readString();
|
|
||||||
endpoint = in.readString();
|
|
||||||
persistentKeepalive = in.readString();
|
|
||||||
preSharedKey = in.readString();
|
|
||||||
publicKey = in.readString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerProxy(final Peer other) {
|
|
||||||
allowedIps = Attribute.join(other.getAllowedIps());
|
|
||||||
endpoint = other.getEndpoint().map(InetEndpoint::toString).orElse("");
|
|
||||||
persistentKeepalive = other.getPersistentKeepalive().map(String::valueOf).orElse("");
|
|
||||||
preSharedKey = other.getPreSharedKey().map(Key::toBase64).orElse("");
|
|
||||||
publicKey = other.getPublicKey().toBase64();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerProxy() {
|
|
||||||
allowedIps = "";
|
|
||||||
endpoint = "";
|
|
||||||
persistentKeepalive = "";
|
|
||||||
preSharedKey = "";
|
|
||||||
publicKey = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind(final ConfigProxy owner) {
|
|
||||||
final InterfaceProxy interfaze = owner.getInterface();
|
|
||||||
final ObservableList<PeerProxy> peers = owner.getPeers();
|
|
||||||
if (interfaceDnsListener == null)
|
|
||||||
interfaceDnsListener = new InterfaceDnsListener(this);
|
|
||||||
interfaze.addOnPropertyChangedCallback(interfaceDnsListener);
|
|
||||||
setInterfaceDns(interfaze.getDnsServers());
|
|
||||||
if (peerListListener == null)
|
|
||||||
peerListListener = new PeerListListener(this);
|
|
||||||
peers.addOnListChangedCallback(peerListListener);
|
|
||||||
setTotalPeers(peers.size());
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateAllowedIpsState() {
|
|
||||||
final AllowedIpsState newState;
|
|
||||||
if (totalPeers == 1) {
|
|
||||||
// String comparison works because we only care if allowedIps is a superset of one of
|
|
||||||
// the above sets of (valid) *networks*. We are not checking for a superset based on
|
|
||||||
// the individual addresses in each set.
|
|
||||||
final Collection<String> networkStrings = getAllowedIpsSet();
|
|
||||||
// If allowedIps contains both the wildcard and the public networks, then private
|
|
||||||
// networks aren't excluded!
|
|
||||||
if (networkStrings.containsAll(IPV4_WILDCARD))
|
|
||||||
newState = AllowedIpsState.CONTAINS_IPV4_WILDCARD;
|
|
||||||
else if (networkStrings.containsAll(IPV4_PUBLIC_NETWORKS))
|
|
||||||
newState = AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS;
|
|
||||||
else
|
|
||||||
newState = AllowedIpsState.OTHER;
|
|
||||||
} else {
|
|
||||||
newState = AllowedIpsState.INVALID;
|
|
||||||
}
|
|
||||||
if (newState != allowedIpsState) {
|
|
||||||
allowedIpsState = newState;
|
|
||||||
notifyPropertyChanged(BR.ableToExcludePrivateIps);
|
|
||||||
notifyPropertyChanged(BR.excludingPrivateIps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getAllowedIps() {
|
|
||||||
return allowedIps;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getAllowedIpsSet() {
|
|
||||||
return new LinkedHashSet<>(Lists.of(Attribute.split(allowedIps)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getEndpoint() {
|
|
||||||
return endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getPersistentKeepalive() {
|
|
||||||
return persistentKeepalive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getPreSharedKey() {
|
|
||||||
return preSharedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public String getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public boolean isAbleToExcludePrivateIps() {
|
|
||||||
return allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS
|
|
||||||
|| allowedIpsState == AllowedIpsState.CONTAINS_IPV4_WILDCARD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bindable
|
|
||||||
public boolean isExcludingPrivateIps() {
|
|
||||||
return allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Peer resolve() throws BadConfigException {
|
|
||||||
final Peer.Builder builder = new Peer.Builder();
|
|
||||||
if (!allowedIps.isEmpty())
|
|
||||||
builder.parseAllowedIPs(allowedIps);
|
|
||||||
if (!endpoint.isEmpty())
|
|
||||||
builder.parseEndpoint(endpoint);
|
|
||||||
if (!persistentKeepalive.isEmpty())
|
|
||||||
builder.parsePersistentKeepalive(persistentKeepalive);
|
|
||||||
if (!preSharedKey.isEmpty())
|
|
||||||
builder.parsePreSharedKey(preSharedKey);
|
|
||||||
if (!publicKey.isEmpty())
|
|
||||||
builder.parsePublicKey(publicKey);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAllowedIps(final String allowedIps) {
|
|
||||||
this.allowedIps = allowedIps;
|
|
||||||
notifyPropertyChanged(BR.allowedIps);
|
|
||||||
calculateAllowedIpsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEndpoint(final String endpoint) {
|
|
||||||
this.endpoint = endpoint;
|
|
||||||
notifyPropertyChanged(BR.endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExcludingPrivateIps(final boolean excludingPrivateIps) {
|
|
||||||
if (!isAbleToExcludePrivateIps() || isExcludingPrivateIps() == excludingPrivateIps)
|
|
||||||
return;
|
|
||||||
final Set<String> oldNetworks = excludingPrivateIps ? IPV4_WILDCARD : IPV4_PUBLIC_NETWORKS;
|
|
||||||
final Set<String> newNetworks = excludingPrivateIps ? IPV4_PUBLIC_NETWORKS : IPV4_WILDCARD;
|
|
||||||
final Collection<String> input = getAllowedIpsSet();
|
|
||||||
final int outputSize = input.size() - oldNetworks.size() + newNetworks.size();
|
|
||||||
final Collection<String> output = new LinkedHashSet<>(outputSize);
|
|
||||||
boolean replaced = false;
|
|
||||||
// Replace the first instance of the wildcard with the public network list, or vice versa.
|
|
||||||
for (final String network : input) {
|
|
||||||
if (oldNetworks.contains(network)) {
|
|
||||||
if (!replaced) {
|
|
||||||
for (final String replacement : newNetworks)
|
|
||||||
if (!output.contains(replacement))
|
|
||||||
output.add(replacement);
|
|
||||||
replaced = true;
|
|
||||||
}
|
|
||||||
} else if (!output.contains(network)) {
|
|
||||||
output.add(network);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// DNS servers only need to handled specially when we're excluding private IPs.
|
|
||||||
if (excludingPrivateIps)
|
|
||||||
output.addAll(dnsRoutes);
|
|
||||||
else
|
|
||||||
output.removeAll(dnsRoutes);
|
|
||||||
allowedIps = Attribute.join(output);
|
|
||||||
allowedIpsState = excludingPrivateIps ?
|
|
||||||
AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS : AllowedIpsState.CONTAINS_IPV4_WILDCARD;
|
|
||||||
notifyPropertyChanged(BR.allowedIps);
|
|
||||||
notifyPropertyChanged(BR.excludingPrivateIps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setInterfaceDns(final CharSequence dnsServers) {
|
|
||||||
final List<String> newDnsRoutes = Stream.of(Attribute.split(dnsServers))
|
|
||||||
.filter(server -> !server.contains(":"))
|
|
||||||
.map(server -> server + "/32")
|
|
||||||
.collect(Collectors.toUnmodifiableList());
|
|
||||||
if (allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS) {
|
|
||||||
final Collection<String> input = getAllowedIpsSet();
|
|
||||||
final Collection<String> output = new LinkedHashSet<>(input.size() + 1);
|
|
||||||
// Yes, this is quadratic in the number of DNS servers, but most users have 1 or 2.
|
|
||||||
for (final String network : input)
|
|
||||||
if (!dnsRoutes.contains(network) || newDnsRoutes.contains(network))
|
|
||||||
output.add(network);
|
|
||||||
// Since output is a Set, this does the Right Thing™ (it does not duplicate networks).
|
|
||||||
output.addAll(newDnsRoutes);
|
|
||||||
// None of the public networks are /32s, so this cannot change the AllowedIPs state.
|
|
||||||
allowedIps = Attribute.join(output);
|
|
||||||
notifyPropertyChanged(BR.allowedIps);
|
|
||||||
}
|
|
||||||
dnsRoutes.clear();
|
|
||||||
dnsRoutes.addAll(newDnsRoutes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPersistentKeepalive(final String persistentKeepalive) {
|
|
||||||
this.persistentKeepalive = persistentKeepalive;
|
|
||||||
notifyPropertyChanged(BR.persistentKeepalive);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPreSharedKey(final String preSharedKey) {
|
|
||||||
this.preSharedKey = preSharedKey;
|
|
||||||
notifyPropertyChanged(BR.preSharedKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPublicKey(final String publicKey) {
|
|
||||||
this.publicKey = publicKey;
|
|
||||||
notifyPropertyChanged(BR.publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTotalPeers(final int totalPeers) {
|
|
||||||
if (this.totalPeers == totalPeers)
|
|
||||||
return;
|
|
||||||
this.totalPeers = totalPeers;
|
|
||||||
calculateAllowedIpsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
if (owner == null)
|
|
||||||
return;
|
|
||||||
final InterfaceProxy interfaze = owner.getInterface();
|
|
||||||
final ObservableList<PeerProxy> peers = owner.getPeers();
|
|
||||||
if (interfaceDnsListener != null)
|
|
||||||
interfaze.removeOnPropertyChangedCallback(interfaceDnsListener);
|
|
||||||
if (peerListListener != null)
|
|
||||||
peers.removeOnListChangedCallback(peerListListener);
|
|
||||||
peers.remove(this);
|
|
||||||
setInterfaceDns("");
|
|
||||||
setTotalPeers(0);
|
|
||||||
owner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
|
||||||
dest.writeString(allowedIps);
|
|
||||||
dest.writeString(endpoint);
|
|
||||||
dest.writeString(persistentKeepalive);
|
|
||||||
dest.writeString(preSharedKey);
|
|
||||||
dest.writeString(publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum AllowedIpsState {
|
|
||||||
CONTAINS_IPV4_PUBLIC_NETWORKS,
|
|
||||||
CONTAINS_IPV4_WILDCARD,
|
|
||||||
INVALID,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class InterfaceDnsListener extends Observable.OnPropertyChangedCallback {
|
|
||||||
private final WeakReference<PeerProxy> weakPeerProxy;
|
|
||||||
|
|
||||||
private InterfaceDnsListener(final PeerProxy peerProxy) {
|
|
||||||
weakPeerProxy = new WeakReference<>(peerProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPropertyChanged(final Observable sender, final int propertyId) {
|
|
||||||
@Nullable final PeerProxy peerProxy = weakPeerProxy.get();
|
|
||||||
if (peerProxy == null) {
|
|
||||||
sender.removeOnPropertyChangedCallback(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// This shouldn't be possible, but try to avoid a ClassCastException anyway.
|
|
||||||
if (!(sender instanceof InterfaceProxy))
|
|
||||||
return;
|
|
||||||
if (!(propertyId == BR._all || propertyId == BR.dnsServers))
|
|
||||||
return;
|
|
||||||
peerProxy.setInterfaceDns(((InterfaceProxy) sender).getDnsServers());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class PeerListListener
|
|
||||||
extends ObservableList.OnListChangedCallback<ObservableList<PeerProxy>> {
|
|
||||||
private final WeakReference<PeerProxy> weakPeerProxy;
|
|
||||||
|
|
||||||
private PeerListListener(final PeerProxy peerProxy) {
|
|
||||||
weakPeerProxy = new WeakReference<>(peerProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChanged(final ObservableList<PeerProxy> sender) {
|
|
||||||
@Nullable final PeerProxy peerProxy = weakPeerProxy.get();
|
|
||||||
if (peerProxy == null) {
|
|
||||||
sender.removeOnListChangedCallback(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
peerProxy.setTotalPeers(sender.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemRangeChanged(final ObservableList<PeerProxy> sender,
|
|
||||||
final int positionStart, final int itemCount) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemRangeInserted(final ObservableList<PeerProxy> sender,
|
|
||||||
final int positionStart, final int itemCount) {
|
|
||||||
onChanged(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemRangeMoved(final ObservableList<PeerProxy> sender,
|
|
||||||
final int fromPosition, final int toPosition,
|
|
||||||
final int itemCount) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemRangeRemoved(final ObservableList<PeerProxy> sender,
|
|
||||||
final int positionStart, final int itemCount) {
|
|
||||||
onChanged(sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PeerProxyCreator implements Parcelable.Creator<PeerProxy> {
|
|
||||||
@Override
|
|
||||||
public PeerProxy createFromParcel(final Parcel in) {
|
|
||||||
return new PeerProxy(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PeerProxy[] newArray(final int size) {
|
|
||||||
return new PeerProxy[size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
288
ui/src/main/java/com/wireguard/android/viewmodel/PeerProxy.kt
Normal file
288
ui/src/main/java/com/wireguard/android/viewmodel/PeerProxy.kt
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package com.wireguard.android.viewmodel
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.databinding.BaseObservable
|
||||||
|
import androidx.databinding.Bindable
|
||||||
|
import androidx.databinding.Observable
|
||||||
|
import androidx.databinding.Observable.OnPropertyChangedCallback
|
||||||
|
import androidx.databinding.ObservableList
|
||||||
|
import com.wireguard.android.BR
|
||||||
|
import com.wireguard.config.Attribute
|
||||||
|
import com.wireguard.config.BadConfigException
|
||||||
|
import com.wireguard.config.Peer
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.LinkedHashSet
|
||||||
|
|
||||||
|
class PeerProxy : BaseObservable, Parcelable {
|
||||||
|
private val dnsRoutes: MutableList<String?> = ArrayList()
|
||||||
|
private var allowedIpsState = AllowedIpsState.INVALID
|
||||||
|
private var interfaceDnsListener: InterfaceDnsListener? = null
|
||||||
|
private var peerListListener: PeerListListener? = null
|
||||||
|
private var owner: ConfigProxy? = null
|
||||||
|
private var totalPeers = 0
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var allowedIps: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.allowedIps)
|
||||||
|
calculateAllowedIpsState()
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var endpoint: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var persistentKeepalive: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.persistentKeepalive)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var preSharedKey: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.preSharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var publicKey: String = ""
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyPropertyChanged(BR.publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
val isAbleToExcludePrivateIps: Boolean
|
||||||
|
get() = allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS || allowedIpsState == AllowedIpsState.CONTAINS_IPV4_WILDCARD
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
val isExcludingPrivateIps: Boolean
|
||||||
|
get() = allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) {
|
||||||
|
allowedIps = parcel.readString() ?: ""
|
||||||
|
endpoint = parcel.readString() ?: ""
|
||||||
|
persistentKeepalive = parcel.readString() ?: ""
|
||||||
|
preSharedKey = parcel.readString() ?: ""
|
||||||
|
publicKey = parcel.readString() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(other: Peer) {
|
||||||
|
allowedIps = Attribute.join(other.allowedIps)
|
||||||
|
endpoint = other.endpoint.map { it.toString() }.orElse("")
|
||||||
|
persistentKeepalive = other.persistentKeepalive.map { it.toString() }.orElse("")
|
||||||
|
preSharedKey = other.preSharedKey.map { it.toBase64() }.orElse("")
|
||||||
|
publicKey = other.publicKey.toBase64()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
fun bind(owner: ConfigProxy) {
|
||||||
|
val interfaze: InterfaceProxy = owner.`interface`
|
||||||
|
val peers = owner.peers
|
||||||
|
if (interfaceDnsListener == null) interfaceDnsListener = InterfaceDnsListener(this)
|
||||||
|
interfaze.addOnPropertyChangedCallback(interfaceDnsListener!!)
|
||||||
|
setInterfaceDns(interfaze.dnsServers)
|
||||||
|
if (peerListListener == null) peerListListener = PeerListListener(this)
|
||||||
|
peers.addOnListChangedCallback(peerListListener)
|
||||||
|
setTotalPeers(peers.size)
|
||||||
|
this.owner = owner
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateAllowedIpsState() {
|
||||||
|
val newState: AllowedIpsState
|
||||||
|
newState = if (totalPeers == 1) {
|
||||||
|
// String comparison works because we only care if allowedIps is a superset of one of
|
||||||
|
// the above sets of (valid) *networks*. We are not checking for a superset based on
|
||||||
|
// the individual addresses in each set.
|
||||||
|
val networkStrings: Collection<String> = getAllowedIpsSet()
|
||||||
|
// If allowedIps contains both the wildcard and the public networks, then private
|
||||||
|
// networks aren't excluded!
|
||||||
|
if (networkStrings.containsAll(IPV4_WILDCARD))
|
||||||
|
AllowedIpsState.CONTAINS_IPV4_WILDCARD
|
||||||
|
else if (networkStrings.containsAll(IPV4_PUBLIC_NETWORKS))
|
||||||
|
AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS
|
||||||
|
else
|
||||||
|
AllowedIpsState.OTHER
|
||||||
|
} else {
|
||||||
|
AllowedIpsState.INVALID
|
||||||
|
}
|
||||||
|
if (newState != allowedIpsState) {
|
||||||
|
allowedIpsState = newState
|
||||||
|
notifyPropertyChanged(BR.ableToExcludePrivateIps)
|
||||||
|
notifyPropertyChanged(BR.excludingPrivateIps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents() = 0
|
||||||
|
|
||||||
|
private fun getAllowedIpsSet() = setOf(*Attribute.split(allowedIps))
|
||||||
|
|
||||||
|
// Replace the first instance of the wildcard with the public network list, or vice versa.
|
||||||
|
// DNS servers only need to handled specially when we're excluding private IPs.
|
||||||
|
fun setExcludingPrivateIps(excludingPrivateIps: Boolean) {
|
||||||
|
if (!isAbleToExcludePrivateIps || isExcludingPrivateIps == excludingPrivateIps) return
|
||||||
|
val oldNetworks = if (excludingPrivateIps) IPV4_WILDCARD else IPV4_PUBLIC_NETWORKS
|
||||||
|
val newNetworks = if (excludingPrivateIps) IPV4_PUBLIC_NETWORKS else IPV4_WILDCARD
|
||||||
|
val input: Collection<String> = getAllowedIpsSet()
|
||||||
|
val outputSize = input.size - oldNetworks.size + newNetworks.size
|
||||||
|
val output: MutableCollection<String?> = LinkedHashSet(outputSize)
|
||||||
|
var replaced = false
|
||||||
|
// Replace the first instance of the wildcard with the public network list, or vice versa.
|
||||||
|
for (network in input) {
|
||||||
|
if (oldNetworks.contains(network)) {
|
||||||
|
if (!replaced) {
|
||||||
|
for (replacement in newNetworks) if (!output.contains(replacement)) output.add(replacement)
|
||||||
|
replaced = true
|
||||||
|
}
|
||||||
|
} else if (!output.contains(network)) {
|
||||||
|
output.add(network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DNS servers only need to handled specially when we're excluding private IPs.
|
||||||
|
if (excludingPrivateIps) output.addAll(dnsRoutes) else output.removeAll(dnsRoutes)
|
||||||
|
allowedIps = Attribute.join(output)
|
||||||
|
allowedIpsState = if (excludingPrivateIps) AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS else AllowedIpsState.CONTAINS_IPV4_WILDCARD
|
||||||
|
notifyPropertyChanged(BR.allowedIps)
|
||||||
|
notifyPropertyChanged(BR.excludingPrivateIps)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(BadConfigException::class)
|
||||||
|
fun resolve(): Peer {
|
||||||
|
val builder = Peer.Builder()
|
||||||
|
if (allowedIps.isNotEmpty()) builder.parseAllowedIPs(allowedIps)
|
||||||
|
if (endpoint.isNotEmpty()) builder.parseEndpoint(endpoint)
|
||||||
|
if (persistentKeepalive.isNotEmpty()) builder.parsePersistentKeepalive(persistentKeepalive)
|
||||||
|
if (preSharedKey.isNotEmpty()) builder.parsePreSharedKey(preSharedKey)
|
||||||
|
if (publicKey.isNotEmpty()) builder.parsePublicKey(publicKey)
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setInterfaceDns(dnsServers: CharSequence) {
|
||||||
|
val newDnsRoutes = Attribute.split(dnsServers).filter { !it.contains(":") }.map { "$it/32" }
|
||||||
|
if (allowedIpsState == AllowedIpsState.CONTAINS_IPV4_PUBLIC_NETWORKS) {
|
||||||
|
val input = getAllowedIpsSet()
|
||||||
|
// Yes, this is quadratic in the number of DNS servers, but most users have 1 or 2.
|
||||||
|
val output = input.filter { !dnsRoutes.contains(it) || newDnsRoutes.contains(it) }.plus(newDnsRoutes).distinct()
|
||||||
|
// None of the public networks are /32s, so this cannot change the AllowedIPs state.
|
||||||
|
allowedIps = Attribute.join(output)
|
||||||
|
notifyPropertyChanged(BR.allowedIps)
|
||||||
|
}
|
||||||
|
dnsRoutes.clear()
|
||||||
|
dnsRoutes.addAll(newDnsRoutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTotalPeers(totalPeers: Int) {
|
||||||
|
if (this.totalPeers == totalPeers) return
|
||||||
|
this.totalPeers = totalPeers
|
||||||
|
calculateAllowedIpsState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unbind() {
|
||||||
|
if (owner == null) return
|
||||||
|
val interfaze: InterfaceProxy = owner!!.`interface`
|
||||||
|
val peers = owner!!.peers
|
||||||
|
if (interfaceDnsListener != null) interfaze.removeOnPropertyChangedCallback(interfaceDnsListener!!)
|
||||||
|
if (peerListListener != null) peers.removeOnListChangedCallback(peerListListener)
|
||||||
|
peers.remove(this)
|
||||||
|
setInterfaceDns("")
|
||||||
|
setTotalPeers(0)
|
||||||
|
owner = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
dest.writeString(allowedIps)
|
||||||
|
dest.writeString(endpoint)
|
||||||
|
dest.writeString(persistentKeepalive)
|
||||||
|
dest.writeString(preSharedKey)
|
||||||
|
dest.writeString(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class AllowedIpsState {
|
||||||
|
CONTAINS_IPV4_PUBLIC_NETWORKS, CONTAINS_IPV4_WILDCARD, INVALID, OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InterfaceDnsListener constructor(peerProxy: PeerProxy) : OnPropertyChangedCallback() {
|
||||||
|
private val weakPeerProxy: WeakReference<PeerProxy> = WeakReference(peerProxy)
|
||||||
|
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
|
||||||
|
val peerProxy = weakPeerProxy.get()
|
||||||
|
if (peerProxy == null) {
|
||||||
|
sender.removeOnPropertyChangedCallback(this)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// This shouldn't be possible, but try to avoid a ClassCastException anyway.
|
||||||
|
if (sender !is InterfaceProxy) return
|
||||||
|
if (!(propertyId == BR._all || propertyId == BR.dnsServers)) return
|
||||||
|
peerProxy.setInterfaceDns(sender.dnsServers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PeerListListener(peerProxy: PeerProxy) : ObservableList.OnListChangedCallback<ObservableList<PeerProxy?>>() {
|
||||||
|
private val weakPeerProxy: WeakReference<PeerProxy> = WeakReference(peerProxy)
|
||||||
|
override fun onChanged(sender: ObservableList<PeerProxy?>) {
|
||||||
|
val peerProxy = weakPeerProxy.get()
|
||||||
|
if (peerProxy == null) {
|
||||||
|
sender.removeOnListChangedCallback(this)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peerProxy.setTotalPeers(sender.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeChanged(sender: ObservableList<PeerProxy?>,
|
||||||
|
positionStart: Int, itemCount: Int) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeInserted(sender: ObservableList<PeerProxy?>,
|
||||||
|
positionStart: Int, itemCount: Int) {
|
||||||
|
onChanged(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeMoved(sender: ObservableList<PeerProxy?>,
|
||||||
|
fromPosition: Int, toPosition: Int,
|
||||||
|
itemCount: Int) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeRemoved(sender: ObservableList<PeerProxy?>,
|
||||||
|
positionStart: Int, itemCount: Int) {
|
||||||
|
onChanged(sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PeerProxyCreator : Parcelable.Creator<PeerProxy> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): PeerProxy {
|
||||||
|
return PeerProxy(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<PeerProxy?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val CREATOR: Parcelable.Creator<PeerProxy> = PeerProxyCreator()
|
||||||
|
private val IPV4_PUBLIC_NETWORKS = setOf(
|
||||||
|
"0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", "32.0.0.0/3",
|
||||||
|
"64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", "172.0.0.0/12",
|
||||||
|
"172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", "173.0.0.0/8", "174.0.0.0/7",
|
||||||
|
"176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", "192.160.0.0/13", "192.169.0.0/16",
|
||||||
|
"192.170.0.0/15", "192.172.0.0/14", "192.176.0.0/12", "192.192.0.0/10",
|
||||||
|
"193.0.0.0/8", "194.0.0.0/7", "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4"
|
||||||
|
)
|
||||||
|
private val IPV4_WILDCARD = setOf("0.0.0.0/0")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user