From 55219121b127a000b2d410a841b6441a25adb1f3 Mon Sep 17 00:00:00 2001 From: Norbel Ambanumben Date: Fri, 11 Oct 2024 11:54:39 +0000 Subject: feat: add invite code to UI. --- .../leap/bitmaskclient/base/models/Introducer.java | 105 +++++++++++++++++++++ .../leap/bitmaskclient/base/models/Provider.java | 12 +++ 2 files changed, 117 insertions(+) create mode 100644 app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java (limited to 'app/src/main/java/se/leap/bitmaskclient/base') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java new file mode 100644 index 00000000..c31912d9 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Introducer.java @@ -0,0 +1,105 @@ +package se.leap.bitmaskclient.base.models; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.net.URI; +import java.net.URISyntaxException; + +public class Introducer implements Parcelable { + private String type; + private String address; + private String certificate; + private String fullyQualifiedDomainName; + private boolean kcpEnabled; + + public Introducer(String type, String address, String certificate, String fullyQualifiedDomainName, boolean kcpEnabled) { + this.type = type; + this.address = address; + this.certificate = certificate; + this.fullyQualifiedDomainName = fullyQualifiedDomainName; + this.kcpEnabled = kcpEnabled; + } + + protected Introducer(Parcel in) { + type = in.readString(); + address = in.readString(); + certificate = in.readString(); + fullyQualifiedDomainName = in.readString(); + kcpEnabled = in.readByte() != 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(type); + dest.writeString(address); + dest.writeString(certificate); + dest.writeString(fullyQualifiedDomainName); + dest.writeByte((byte) (kcpEnabled ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator<>() { + @Override + public Introducer createFromParcel(Parcel in) { + return new Introducer(in); + } + + @Override + public Introducer[] newArray(int size) { + return new Introducer[size]; + } + }; + + public boolean validate() { + if (!"obfsvpnintro".equals(type)) { + throw new IllegalArgumentException("Unknown type: " + type); + } + if (!address.contains(":") || address.split(":").length != 2) { + throw new IllegalArgumentException("Expected address in format ipaddr:port"); + } + if (certificate.length() != 70) { + throw new IllegalArgumentException("Wrong certificate length: " + certificate.length()); + } + if (!"localhost".equals(fullyQualifiedDomainName) && fullyQualifiedDomainName.split("\\.").length < 2) { + throw new IllegalArgumentException("Expected a FQDN, got: " + fullyQualifiedDomainName); + } + return true; + } + + public static Introducer fromUrl(String introducerUrl) throws URISyntaxException { + URI uri = new URI(introducerUrl); + String fqdn = getQueryParam(uri, "fqdn"); + if (fqdn == null || fqdn.isEmpty()) { + throw new IllegalArgumentException("FQDN not found in the introducer URL"); + } + + boolean kcp = "1".equals(getQueryParam(uri, "kcp")); + + String cert = getQueryParam(uri, "cert"); + if (cert == null || cert.isEmpty()) { + throw new IllegalArgumentException("Cert not found in the introducer URL"); + } + + return new Introducer(uri.getScheme(), uri.getAuthority(), cert, fqdn, kcp); + } + + public String toUrl() { + return String.format("%s://%s?fqdn=%s&kcp=%d&cert=%s", type, address, fullyQualifiedDomainName, kcpEnabled ? 1 : 0, certificate); + } + + private static String getQueryParam(URI uri, String param) { + String[] queryParams = uri.getQuery().split("&"); + for (String queryParam : queryParams) { + String[] keyValue = queryParam.split("="); + if (keyValue.length == 2 && keyValue[0].equals(param)) { + return keyValue[1]; + } + } + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java index 725c602a..bf1e6b94 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Provider.java @@ -86,6 +86,7 @@ public final class Provider implements Parcelable { private long lastGeoIpUpdate = 0L; private long lastMotdUpdate = 0L; private long lastMotdSeen = 0L; + private Introducer introducer = null; private Set lastMotdSeenHashes = new HashSet<>(); private boolean shouldUpdateVpnCertificate; @@ -115,6 +116,11 @@ public final class Provider implements Parcelable { public Provider() { } + public Provider(Introducer introducer) { + this(introducer.toUrl(), null); + this.introducer = introducer; + } + public Provider(String mainUrl) { this(mainUrl, null); } @@ -423,6 +429,7 @@ public final class Provider implements Parcelable { parcel.writeLong(lastMotdSeen); parcel.writeStringList(new ArrayList<>(lastMotdSeenHashes)); parcel.writeInt(shouldUpdateVpnCertificate ? 0 : 1); + parcel.writeParcelable(introducer, 0); } @@ -484,6 +491,7 @@ public final class Provider implements Parcelable { in.readStringList(lastMotdSeenHashes); this.lastMotdSeenHashes = new HashSet<>(lastMotdSeenHashes); this.shouldUpdateVpnCertificate = in.readInt() == 0; + this.introducer = in.readParcelable(Introducer.class.getClassLoader()); } catch (MalformedURLException | JSONException e) { e.printStackTrace(); } @@ -739,6 +747,10 @@ public final class Provider implements Parcelable { return getCertificatePinEncoding() + ":" + getCertificatePin(); } + public boolean hasIntroducer() { + return introducer != null; + } + /** * resets everything except the main url, the providerIp and the geoip * service url (currently preseeded) -- cgit v1.2.3