2018-05-02 17:29:58 +02:00
|
|
|
/*
|
2018-09-07 05:32:46 +02:00
|
|
|
* Copyright © 2017-2018 WireGuard LLC. All Rights Reserved.
|
2018-07-06 04:09:48 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2018-05-02 17:29:58 +02:00
|
|
|
*/
|
|
|
|
|
2017-07-29 13:08:54 +02:00
|
|
|
package com.wireguard.config;
|
|
|
|
|
2018-07-13 02:10:35 +02:00
|
|
|
import android.support.annotation.Nullable;
|
2017-08-01 04:13:01 +02:00
|
|
|
|
2017-07-29 13:08:54 +02:00
|
|
|
import java.io.BufferedReader;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.InputStreamReader;
|
2018-04-28 04:45:17 +02:00
|
|
|
import java.util.ArrayList;
|
2018-09-06 03:17:14 +02:00
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.LinkedHashSet;
|
2018-04-28 04:45:17 +02:00
|
|
|
import java.util.List;
|
2018-09-06 03:17:14 +02:00
|
|
|
import java.util.Objects;
|
|
|
|
import java.util.Set;
|
2017-07-29 13:08:54 +02:00
|
|
|
|
|
|
|
/**
|
2018-09-06 03:17:14 +02:00
|
|
|
* Represents the contents of a wg-quick configuration file, made up of one or more "Interface"
|
|
|
|
* sections (combined together), and zero or more "Peer" sections (treated individually).
|
|
|
|
* <p>
|
|
|
|
* Instances of this class are immutable.
|
2017-07-29 13:08:54 +02:00
|
|
|
*/
|
2018-09-06 03:17:14 +02:00
|
|
|
public final class Config {
|
|
|
|
private final Interface interfaze;
|
|
|
|
private final List<Peer> peers;
|
|
|
|
|
|
|
|
private Config(final Builder builder) {
|
|
|
|
interfaze = Objects.requireNonNull(builder.interfaze, "An [Interface] section is required");
|
|
|
|
// Defensively copy to ensure immutability even if the Builder is reused.
|
|
|
|
peers = Collections.unmodifiableList(new ArrayList<>(builder.peers));
|
2018-07-24 19:32:40 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
/**
|
|
|
|
* Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
|
|
|
|
* {@link ParseException} if the input is not well-formed or contains unparseable sections.
|
|
|
|
*
|
|
|
|
* @param stream a stream of UTF-8 text that is interpreted as a WireGuard configuration file
|
|
|
|
* @return a {@code Config} instance representing the supplied configuration
|
|
|
|
*/
|
|
|
|
public static Config parse(final InputStream stream) throws IOException, ParseException {
|
2018-12-08 03:47:43 +01:00
|
|
|
return parse(new BufferedReader(new InputStreamReader(stream)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
|
|
|
|
* {@link ParseException} if the input is not well-formed or contains unparseable sections.
|
|
|
|
*
|
|
|
|
* @param reader a BufferedReader of UTF-8 text that is interpreted as a WireGuard configuration file
|
|
|
|
* @return a {@code Config} instance representing the supplied configuration
|
|
|
|
*/
|
|
|
|
public static Config parse(final BufferedReader reader) throws IOException, ParseException {
|
2018-09-06 03:17:14 +02:00
|
|
|
final Builder builder = new Builder();
|
2018-12-08 03:47:43 +01:00
|
|
|
final Collection<String> interfaceLines = new ArrayList<>();
|
|
|
|
final Collection<String> peerLines = new ArrayList<>();
|
|
|
|
boolean inInterfaceSection = false;
|
|
|
|
boolean inPeerSection = false;
|
|
|
|
@Nullable String line;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
final int commentIndex = line.indexOf('#');
|
|
|
|
if (commentIndex != -1)
|
|
|
|
line = line.substring(0, commentIndex);
|
|
|
|
line = line.trim();
|
|
|
|
if (line.isEmpty())
|
|
|
|
continue;
|
|
|
|
if (line.startsWith("[")) {
|
|
|
|
// Consume all [Peer] lines read so far.
|
|
|
|
if (inPeerSection) {
|
|
|
|
builder.parsePeer(peerLines);
|
|
|
|
peerLines.clear();
|
|
|
|
}
|
|
|
|
if ("[Interface]".equalsIgnoreCase(line)) {
|
|
|
|
inInterfaceSection = true;
|
|
|
|
inPeerSection = false;
|
|
|
|
} else if ("[Peer]".equalsIgnoreCase(line)) {
|
|
|
|
inInterfaceSection = false;
|
|
|
|
inPeerSection = true;
|
2018-09-06 03:17:14 +02:00
|
|
|
} else {
|
2018-12-08 03:47:43 +01:00
|
|
|
throw new ParseException("top level", line, "Unknown section name");
|
2018-09-06 03:17:14 +02:00
|
|
|
}
|
2018-12-08 03:47:43 +01:00
|
|
|
} else if (inInterfaceSection) {
|
|
|
|
interfaceLines.add(line);
|
|
|
|
} else if (inPeerSection) {
|
|
|
|
peerLines.add(line);
|
|
|
|
} else {
|
|
|
|
throw new ParseException("top level", line, "Expected [Interface] or [Peer]");
|
2017-11-26 23:45:41 +01:00
|
|
|
}
|
2017-07-30 00:30:33 +02:00
|
|
|
}
|
2018-12-08 03:47:43 +01:00
|
|
|
if (inPeerSection)
|
|
|
|
builder.parsePeer(peerLines);
|
|
|
|
else if (!inInterfaceSection)
|
|
|
|
throw new ParseException("top level", "", "Empty configuration");
|
|
|
|
// Combine all [Interface] sections in the file.
|
|
|
|
builder.parseInterface(interfaceLines);
|
2018-09-06 03:17:14 +02:00
|
|
|
return builder.build();
|
2017-07-29 13:08:54 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
@Override
|
|
|
|
public boolean equals(final Object obj) {
|
|
|
|
if (!(obj instanceof Config))
|
|
|
|
return false;
|
|
|
|
final Config other = (Config) obj;
|
|
|
|
return interfaze.equals(other.interfaze) && peers.equals(other.peers);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the interface section of the configuration.
|
|
|
|
*
|
|
|
|
* @return the interface configuration
|
|
|
|
*/
|
2018-01-01 09:06:37 +01:00
|
|
|
public Interface getInterface() {
|
2018-09-06 03:17:14 +02:00
|
|
|
return interfaze;
|
2017-08-16 11:26:45 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
/**
|
|
|
|
* Returns a list of the configuration's peer sections.
|
|
|
|
*
|
|
|
|
* @return a list of {@link Peer}s
|
|
|
|
*/
|
2018-04-28 04:45:17 +02:00
|
|
|
public List<Peer> getPeers() {
|
2018-01-01 09:06:37 +01:00
|
|
|
return peers;
|
2017-08-08 12:54:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
return 31 * interfaze.hashCode() + peers.hashCode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the {@code Config} into a string suitable for debugging purposes. The {@code Config}
|
|
|
|
* is identified by its interface's public key and the number of peers it has.
|
|
|
|
*
|
|
|
|
* @return a concise single-line identifier for the {@code Config}
|
|
|
|
*/
|
2017-07-29 13:08:54 +02:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2018-09-06 03:17:14 +02:00
|
|
|
return "(Config " + interfaze + " (" + peers.size() + " peers))";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the {@code Config} into a string suitable for use as a {@code wg-quick}
|
|
|
|
* configuration file.
|
|
|
|
*
|
|
|
|
* @return the {@code Config} represented as one [Interface] and zero or more [Peer] sections
|
|
|
|
*/
|
|
|
|
public String toWgQuickString() {
|
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append("[Interface]\n").append(interfaze.toWgQuickString());
|
2017-08-13 14:24:03 +02:00
|
|
|
for (final Peer peer : peers)
|
2018-09-06 03:17:14 +02:00
|
|
|
sb.append("\n[Peer]\n").append(peer.toWgQuickString());
|
2017-07-30 00:30:33 +02:00
|
|
|
return sb.toString();
|
2017-07-29 13:08:54 +02:00
|
|
|
}
|
2018-04-30 18:39:12 +02:00
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
/**
|
|
|
|
* Serializes the {@code Config} for use with the WireGuard cross-platform userspace API.
|
|
|
|
*
|
|
|
|
* @return the {@code Config} represented as a series of "key=value" lines
|
|
|
|
*/
|
|
|
|
public String toWgUserspaceString() {
|
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(interfaze.toWgUserspaceString());
|
|
|
|
sb.append("replace_peers=true\n");
|
|
|
|
for (final Peer peer : peers)
|
|
|
|
sb.append(peer.toWgUserspaceString());
|
|
|
|
return sb.toString();
|
|
|
|
}
|
2018-04-30 18:39:12 +02:00
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
@SuppressWarnings("UnusedReturnValue")
|
|
|
|
public static final class Builder {
|
|
|
|
// Defaults to an empty set.
|
|
|
|
private final Set<Peer> peers = new LinkedHashSet<>();
|
|
|
|
// No default; must be provided before building.
|
|
|
|
@Nullable private Interface interfaze;
|
2018-04-30 18:39:12 +02:00
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Builder addPeer(final Peer peer) {
|
|
|
|
peers.add(peer);
|
|
|
|
return this;
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Builder addPeers(final Collection<Peer> peers) {
|
|
|
|
this.peers.addAll(peers);
|
|
|
|
return this;
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Config build() {
|
|
|
|
return new Config(this);
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Builder parseInterface(final Iterable<? extends CharSequence> lines) throws ParseException {
|
|
|
|
return setInterface(Interface.parse(lines));
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Builder parsePeer(final Iterable<? extends CharSequence> lines) throws ParseException {
|
|
|
|
return addPeer(Peer.parse(lines));
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 03:17:14 +02:00
|
|
|
public Builder setInterface(final Interface interfaze) {
|
|
|
|
this.interfaze = interfaze;
|
|
|
|
return this;
|
2018-04-30 18:39:12 +02:00
|
|
|
}
|
|
|
|
}
|
2017-07-29 13:08:54 +02:00
|
|
|
}
|