Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8019debd0 | ||
|
|
ab317e76b3 | ||
|
|
15556be265 | ||
|
|
26958e35fe | ||
|
|
e905881238 | ||
|
|
fde7a941b6 | ||
|
|
9d24deabe9 | ||
|
|
c2f331e7ec | ||
|
|
705bddd062 | ||
|
|
826609db30 | ||
|
|
0eca96bd67 | ||
|
|
f767387840 | ||
|
|
8ae3c21a23 | ||
|
|
be1c056502 | ||
|
|
500142eb5b | ||
|
|
1e5876bc08 | ||
|
|
9355d6015f | ||
|
|
700db6dca0 | ||
|
|
f61ba7a6ad | ||
|
|
fe7e8cef41 | ||
|
|
7cda39ad12 | ||
|
|
b377a53de4 | ||
|
|
9a306365b8 | ||
|
|
f74cdbe648 | ||
|
|
6a51c1c106 | ||
|
|
36195be292 | ||
|
|
893fe93ab6 | ||
|
|
90e844b082 | ||
|
|
886b60b2de | ||
|
|
30d8e9884c | ||
|
|
7e05b0752b | ||
|
|
e124c371b3 | ||
|
|
4af4ae1006 | ||
|
|
b63c4a7c68 |
No files matched your search
+1
-1
@@ -6,6 +6,6 @@ deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: j8y4qeDOXAYjmf6WU6EbpUA6AKTz26yJ4zNb6oAuB4h9Y3PpBRjFjCIBL6jJQnMxZMLjdgk72/3uqS7+//jJ8/iiY/OJhO2L+2us8TJDcQhH5EI1USe/lcZKf60iJjmd9Q9pjh1eHNYhxr6kyNLnpa6fw5b/hIBT6CvNZAJV1qbhRpHwtzbgdlMFaHGnpX192vJNw7E3uuJuSwue5XNK2ybDp81oTrM0Q16JCq/UsQhGq4Ba3JffOhdBhpfg4EI2jJfe3vN8CFqojCYKNSp7eL6DPFPaV4x8Ly8cRIOZ5Zb60m5SyWAcagPKJuUq3ZOTHfeVTjkI9ymZMHe1pla2Mq6eitIBwZIGnonjyMxcTdkR9EJgq9jo+1PzdStR/NlGLakBiwPzk8Ov0DZ+iOwpYUkISUkMU0UgKWwoFoRu18CHtp+ALEsoYZhBQGlcShq+4EGv9VMNBYk9vN3KXNRMVGKqXd6O3T6shvSQgoUn/3mvksOTAuhS5J4U3woHecDrXwl/RfP2aqiHhX/S7p7yY4hVcHu8kiSYpNQMbay+rNd6HxFcRe/U9HOUt6kW8/yo1ROyCxs3ze+N6LtGDHspgzkFaay6I6wMHYNDnzzRP4MC3Zc73zxViXol6dGBk4Tawsm16VaI6b1uyWLBGKvuU23azkmMTHUmGFXl2/BTaYw=
|
||||
file: universal/target/nuvotifier-universal-2.3.2.jar
|
||||
file: universal/target/nuvotifier-universal-2.3.4.jar
|
||||
on:
|
||||
tags: true
|
||||
+3
-1
@@ -2,6 +2,8 @@
|
||||
|
||||
NuVotifier is a Bukkit plugin whose purpose is to be notified (aka *votified*) when a vote is made on a Minecraft server top list for the server. NuVotifier creates a *lightweight* server that waits for connections by Minecraft server lists and uses a simple protocol to get the required information. NuVotifier is *secure*, and makes sure that all vote notifications are delivered by authentic top lists.
|
||||
|
||||
[](https://travis-ci.org/NuVotifier/NuVotifier)
|
||||
|
||||
## Why fork Votifier?
|
||||
|
||||
Votifier has lagged in activity, has severe security flaws in its protocol, and is vulnerable to denial of service attacks.
|
||||
@@ -27,7 +29,7 @@ NuVotifier configures itself the first time it is run. If you have an existing V
|
||||
|
||||
If you want to customize NuVotifier, simply the edit `./plugins/(Nu)Votifier/config.yml` file.
|
||||
|
||||
An in-depth configuration guide is located in `usage.md` of this repository.
|
||||
An in-depth configuration guide is located in the [usage](https://github.com/NuVotifier/NuVotifier/blob/master/usage.md) document.
|
||||
|
||||
### Tokens
|
||||
|
||||
|
||||
+4
-5
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -56,10 +56,9 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.7.2-R0.1</version>
|
||||
<type>jar</type>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.8.8-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -258,7 +258,7 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
serverChannel = future.channel();
|
||||
getLogger().info("Votifier enabled.");
|
||||
getLogger().info("Votifier enabled on socket "+serverChannel.localAddress()+".");
|
||||
} else {
|
||||
SocketAddress socketAddress = future.channel().localAddress();
|
||||
if (socketAddress == null) {
|
||||
@@ -342,12 +342,12 @@ public class NuVotifierBukkit extends JavaPlugin implements VoteHandler, Votifie
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
if (debug) {
|
||||
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
|
||||
getLogger().info("Got a protocol v1 vote record -> " + vote);
|
||||
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
} else {
|
||||
getLogger().info("Got a protocol v2 vote record -> " + vote);
|
||||
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
}
|
||||
}
|
||||
Bukkit.getScheduler().runTask(this, new Runnable() {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
serverChannel = future.channel();
|
||||
getLogger().info("Votifier enabled.");
|
||||
getLogger().info("Votifier enabled on socket "+serverChannel.localAddress()+".");
|
||||
} else {
|
||||
SocketAddress socketAddress = future.channel().localAddress();
|
||||
if (socketAddress == null) {
|
||||
@@ -293,7 +293,8 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
|
||||
continue;
|
||||
}
|
||||
ProxyForwardingVoteSource.BackendServer server = new ProxyForwardingVoteSource.BackendServer(s,
|
||||
new InetSocketAddress(address, section.getShort("port")), KeyCreator.createKeyFrom(section.getString("key")));
|
||||
new InetSocketAddress(address, section.getShort("port")),
|
||||
KeyCreator.createKeyFrom(section.getString("token",section.getString("key"))));
|
||||
serverList.add(server);
|
||||
}
|
||||
|
||||
@@ -320,12 +321,12 @@ public class NuVotifier extends Plugin implements VoteHandler, VotifierPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
if (debug) {
|
||||
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
|
||||
getLogger().info("Got a protocol v1 vote record -> " + vote);
|
||||
getLogger().info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
} else {
|
||||
getLogger().info("Got a protocol v2 vote record -> " + vote);
|
||||
getLogger().info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package com.vexsoftware.votifier.bungee.forwarding;
|
||||
|
||||
import com.vexsoftware.votifier.bungee.NuVotifier;
|
||||
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
|
||||
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class AbstractPluginMessagingForwardingSource implements ForwardingVoteSource {
|
||||
|
||||
public AbstractPluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
|
||||
ProxyServer.getInstance().registerChannel(channel);
|
||||
this.channel = channel;
|
||||
this.nuVotifier = nuVotifier;
|
||||
this.cache = cache;
|
||||
this.ignoredServers = ignoredServers;
|
||||
}
|
||||
|
||||
protected AbstractPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
|
||||
this(channel, null, nuVotifier, voteCache);
|
||||
}
|
||||
|
||||
protected final NuVotifier nuVotifier;
|
||||
protected final String channel;
|
||||
protected final VoteCache cache;
|
||||
protected final List<String> ignoredServers;
|
||||
|
||||
@Override
|
||||
public void forward(Vote v) {
|
||||
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
for (ServerInfo s : ProxyServer.getInstance().getServers().values()) {
|
||||
if (ignoredServers != null && ignoredServers.contains(s.getName())) continue;
|
||||
if (!forwardSpecific(s, rawData)) attemptToAddToCache(v, s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean forwardSpecific(ServerInfo connection, Vote vote) {
|
||||
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
return connection.sendData(channel, rawData, false);
|
||||
}
|
||||
|
||||
private boolean forwardSpecific(ServerInfo connection, byte[] data) {
|
||||
return connection.sendData(channel, data, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void halt() {
|
||||
ProxyServer.getInstance().unregisterChannel(channel);
|
||||
|
||||
if (cache != null && cache instanceof FileVoteCache) {
|
||||
try {
|
||||
((FileVoteCache) cache).halt();
|
||||
} catch (IOException e) {
|
||||
nuVotifier.getLogger().log(Level.SEVERE, "Unable to save cached votes, votes will be lost.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleServerConnected(final ServerConnectedEvent e) {
|
||||
if (cache == null) return;
|
||||
final String serverName = e.getServer().getInfo().getName();
|
||||
final Collection<Vote> cachedVotes = cache.evict(serverName);
|
||||
if (!cachedVotes.isEmpty()) {
|
||||
nuVotifier.getProxy().getScheduler().schedule(nuVotifier, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int evicted = 0;
|
||||
int unsuccessfulEvictions = 0;
|
||||
for (Vote v : cachedVotes) {
|
||||
if (forwardSpecific(e.getServer().getInfo(), v)) {
|
||||
evicted++;
|
||||
} else {
|
||||
// Re-add to cache to send later.
|
||||
cache.addToCache(v, serverName);
|
||||
unsuccessfulEvictions++;
|
||||
}
|
||||
}
|
||||
if (nuVotifier.isDebug()) {
|
||||
nuVotifier.getLogger().info("Successfully evicted " + evicted + " votes to server '" + serverName + "'.");
|
||||
if (unsuccessfulEvictions > 0) {
|
||||
nuVotifier.getLogger().info("Held " + unsuccessfulEvictions + " votes for server '" + serverName + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
protected void attemptToAddToCache(Vote v, String server) {
|
||||
if (cache != null) {
|
||||
cache.addToCache(v, server);
|
||||
if (nuVotifier.isDebug())
|
||||
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> " + server);
|
||||
} else if (nuVotifier.isDebug())
|
||||
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -8,16 +8,18 @@ import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
/**
|
||||
* @author Joseph Hirschfeld
|
||||
* @date 12/31/2015
|
||||
*/
|
||||
public final class OnlineForwardPluginMessagingForwardingSource extends PluginMessagingForwardingSource {
|
||||
public final class OnlineForwardPluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
|
||||
public OnlineForwardPluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache cache, String fallbackServer) {
|
||||
super(channel, nuVotifier, cache);
|
||||
this.fallbackServer = fallbackServer;
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
}
|
||||
|
||||
private final String fallbackServer;
|
||||
@@ -27,7 +29,7 @@ public final class OnlineForwardPluginMessagingForwardingSource extends PluginMe
|
||||
ProxiedPlayer p = ProxyServer.getInstance().getPlayer(v.getUsername());
|
||||
if (p == null || !forwardSpecific(p.getServer().getInfo(), v)) {
|
||||
ServerInfo serverInfo = ProxyServer.getInstance().getServers().get(fallbackServer);
|
||||
if (serverInfo == null || !forwardSpecific(serverInfo, v)) {
|
||||
if (serverInfo != null && !forwardSpecific(serverInfo, v)) {
|
||||
attemptToAddToCache(v, fallbackServer);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-93
@@ -1,75 +1,25 @@
|
||||
package com.vexsoftware.votifier.bungee.forwarding;
|
||||
|
||||
import com.vexsoftware.votifier.bungee.NuVotifier;
|
||||
import com.vexsoftware.votifier.bungee.forwarding.cache.FileVoteCache;
|
||||
import com.vexsoftware.votifier.bungee.forwarding.cache.VoteCache;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Created by Joe Hirschfeld on 10/20/2015.
|
||||
*/
|
||||
public class PluginMessagingForwardingSource implements ForwardingVoteSource, Listener {
|
||||
public class PluginMessagingForwardingSource extends AbstractPluginMessagingForwardingSource implements Listener {
|
||||
|
||||
public PluginMessagingForwardingSource(String channel, List<String> ignoredServers, NuVotifier nuVotifier, VoteCache cache) {
|
||||
ProxyServer.getInstance().registerChannel(channel);
|
||||
super(channel, ignoredServers, nuVotifier, cache);
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
this.channel = channel;
|
||||
this.nuVotifier = nuVotifier;
|
||||
this.cache = cache;
|
||||
this.ignoredServers = ignoredServers;
|
||||
}
|
||||
|
||||
protected PluginMessagingForwardingSource(String channel, NuVotifier nuVotifier, VoteCache voteCache) {
|
||||
this(channel, null, nuVotifier, voteCache);
|
||||
}
|
||||
|
||||
protected final NuVotifier nuVotifier;
|
||||
protected final String channel;
|
||||
protected final VoteCache cache;
|
||||
protected final List<String> ignoredServers;
|
||||
|
||||
@Override
|
||||
public void forward(Vote v) {
|
||||
byte[] rawData = v.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
for (ServerInfo s : ProxyServer.getInstance().getServers().values()) {
|
||||
if (ignoredServers != null && ignoredServers.contains(s.getName())) continue;
|
||||
if (!forwardSpecific(s, rawData)) attemptToAddToCache(v, s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean forwardSpecific(ServerInfo connection, Vote vote) {
|
||||
byte[] rawData = vote.serialize().toString().getBytes(StandardCharsets.UTF_8);
|
||||
return connection.sendData(channel, rawData, false);
|
||||
}
|
||||
|
||||
private boolean forwardSpecific(ServerInfo connection, byte[] data) {
|
||||
return connection.sendData(channel, data, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void halt() {
|
||||
ProxyServer.getInstance().unregisterChannel(channel);
|
||||
|
||||
if (cache != null && cache instanceof FileVoteCache) {
|
||||
try {
|
||||
((FileVoteCache) cache).halt();
|
||||
} catch (IOException e) {
|
||||
nuVotifier.getLogger().log(Level.SEVERE, "Unable to save cached votes, votes will be lost.", e);
|
||||
}
|
||||
}
|
||||
super(channel, nuVotifier, voteCache);
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(nuVotifier, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -81,43 +31,4 @@ public class PluginMessagingForwardingSource implements ForwardingVoteSource, Li
|
||||
public void onServerConnected(final ServerConnectedEvent e) { //Attempt to resend any votes that were previously cached.
|
||||
handleServerConnected(e);
|
||||
}
|
||||
|
||||
protected void handleServerConnected(final ServerConnectedEvent e) {
|
||||
if (cache == null) return;
|
||||
final String serverName = e.getServer().getInfo().getName();
|
||||
final Collection<Vote> cachedVotes = cache.evict(serverName);
|
||||
if (!cachedVotes.isEmpty()) {
|
||||
nuVotifier.getProxy().getScheduler().schedule(nuVotifier, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int evicted = 0;
|
||||
int unsuccessfulEvictions = 0;
|
||||
for (Vote v : cachedVotes) {
|
||||
if (forwardSpecific(e.getServer().getInfo(), v)) {
|
||||
evicted++;
|
||||
} else {
|
||||
// Re-add to cache to send later.
|
||||
cache.addToCache(v, serverName);
|
||||
unsuccessfulEvictions++;
|
||||
}
|
||||
}
|
||||
if (nuVotifier.isDebug()) {
|
||||
nuVotifier.getLogger().info("Successfully evicted " + evicted + " votes to server '" + serverName + "'.");
|
||||
if (unsuccessfulEvictions > 0) {
|
||||
nuVotifier.getLogger().info("Held " + unsuccessfulEvictions + " votes for server '" + serverName + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
protected void attemptToAddToCache(Vote v, String server) {
|
||||
if (cache != null) {
|
||||
cache.addToCache(v, server);
|
||||
if (nuVotifier.isDebug())
|
||||
nuVotifier.getLogger().info("Added to forwarding cache: " + v + " -> " + server);
|
||||
} else if (nuVotifier.isDebug())
|
||||
nuVotifier.getLogger().severe("Could not immediately send vote to backend, vote lost! " + v + " -> " + server);
|
||||
}
|
||||
}
|
||||
Vendored
+28
-28
@@ -7,15 +7,11 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@@ -42,37 +38,41 @@ public class FileVoteCache extends MemoryVoteCache {
|
||||
}
|
||||
|
||||
private void load() throws IOException {
|
||||
if (cacheFile.exists()) {
|
||||
// Load the cache from disk
|
||||
JSONObject object = new JSONObject(Files.toString(cacheFile, StandardCharsets.UTF_8));
|
||||
// Load the cache from disk
|
||||
JSONObject object;
|
||||
try (BufferedReader reader = Files.newReader(cacheFile, StandardCharsets.UTF_8)) {
|
||||
object = new JSONObject(new JSONTokener(reader));
|
||||
} catch (FileNotFoundException e) {
|
||||
object = new JSONObject();
|
||||
}
|
||||
|
||||
// Deserialize all votes contained
|
||||
for (Object server : object.keySet()) {
|
||||
JSONArray voteArray = object.optJSONArray(((String) server));
|
||||
if (voteArray == null) continue;
|
||||
Set<Vote> votes = new LinkedHashSet<>(voteArray.length());
|
||||
for (int i = 0; i < voteArray.length(); i++) {
|
||||
votes.add(new Vote(voteArray.getJSONObject(i)));
|
||||
}
|
||||
voteCache.put(((String) server), votes);
|
||||
// Deserialize all votes contained
|
||||
for (Object server : object.keySet()) {
|
||||
JSONArray voteArray = object.getJSONArray(((String) server));
|
||||
List<Vote> votes = new ArrayList<>(voteArray.length());
|
||||
for (int i = 0; i < voteArray.length(); i++) {
|
||||
JSONObject voteObject = voteArray.getJSONObject(i);
|
||||
votes.add(new Vote(voteObject));
|
||||
}
|
||||
} else {
|
||||
cacheFile.createNewFile();
|
||||
voteCache.put(((String) server), votes);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
cacheLock.lock();
|
||||
// Create a copy of the votes.
|
||||
JSONObject votesObject = new JSONObject();
|
||||
for (Map.Entry<String, Collection<Vote>> entry : voteCache.entrySet()) {
|
||||
JSONArray array = new JSONArray();
|
||||
for (Vote vote : entry.getValue()) {
|
||||
array.put(vote.serialize());
|
||||
try {
|
||||
// Create a copy of the votes.
|
||||
for (Map.Entry<String, Collection<Vote>> entry : voteCache.entrySet()) {
|
||||
JSONArray array = new JSONArray();
|
||||
for (Vote vote : entry.getValue()) {
|
||||
array.put(vote.serialize());
|
||||
}
|
||||
votesObject.put(entry.getKey(), array);
|
||||
}
|
||||
votesObject.put(entry.getKey(), array);
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
cacheLock.unlock();
|
||||
|
||||
try (BufferedWriter writer = Files.newWriter(cacheFile, StandardCharsets.UTF_8)) {
|
||||
votesObject.write(writer);
|
||||
|
||||
Vendored
+23
-12
@@ -1,5 +1,6 @@
|
||||
package com.vexsoftware.votifier.bungee.forwarding.cache;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
@@ -22,28 +23,38 @@ public class MemoryVoteCache implements VoteCache {
|
||||
@Override
|
||||
public Collection<String> getCachedServers() {
|
||||
cacheLock.lock();
|
||||
Set<String> servers = ImmutableSet.copyOf(voteCache.keySet());
|
||||
cacheLock.unlock();
|
||||
return servers;
|
||||
try {
|
||||
return ImmutableList.copyOf(voteCache.keySet());
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToCache(Vote v, String server) {
|
||||
if (server == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
Collection<Vote> voteCollection = voteCache.get(server);
|
||||
if (voteCollection == null) {
|
||||
voteCollection = new LinkedHashSet<>();
|
||||
voteCache.put(server, voteCollection);
|
||||
try {
|
||||
Collection<Vote> voteCollection = voteCache.get(server);
|
||||
if (voteCollection == null) {
|
||||
voteCollection = new ArrayList<>();
|
||||
voteCache.put(server, voteCollection);
|
||||
}
|
||||
voteCollection.add(v);
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
voteCollection.add(v);
|
||||
cacheLock.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Vote> evict(String server) {
|
||||
if (server == null) throw new NullPointerException();
|
||||
cacheLock.lock();
|
||||
Collection<Vote> fromCollection = voteCache.remove(server);
|
||||
cacheLock.unlock();
|
||||
return fromCollection != null ? ImmutableSet.copyOf(fromCollection) : ImmutableSet.<Vote>of();
|
||||
try {
|
||||
Collection<Vote> fromCollection = voteCache.remove(server);
|
||||
return fromCollection != null ? fromCollection : ImmutableList.<Vote>of();
|
||||
} finally {
|
||||
cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class ProxyForwardingVoteSource implements ForwardingVoteSource {
|
||||
public void onFailure(Throwable error) {
|
||||
handleFailure(server, v, error, tries);
|
||||
}
|
||||
}));
|
||||
}, plugin));
|
||||
}
|
||||
})
|
||||
.connect(server.address)
|
||||
|
||||
+7
-2
@@ -1,5 +1,6 @@
|
||||
package com.vexsoftware.votifier.bungee.forwarding.proxy.client;
|
||||
|
||||
import com.vexsoftware.votifier.bungee.NuVotifier;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
@@ -8,10 +9,12 @@ import io.netty.handler.codec.CorruptedFrameException;
|
||||
public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandler<String> {
|
||||
private final Vote toSend;
|
||||
private final VotifierResponseHandler responseHandler;
|
||||
private final NuVotifier nuVotifier;
|
||||
|
||||
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler) {
|
||||
public VotifierProtocol2HandshakeHandler(Vote toSend, VotifierResponseHandler responseHandler, NuVotifier nuVotifier) {
|
||||
this.toSend = toSend;
|
||||
this.responseHandler = responseHandler;
|
||||
this.nuVotifier = nuVotifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -22,7 +25,9 @@ public class VotifierProtocol2HandshakeHandler extends SimpleChannelInboundHandl
|
||||
}
|
||||
|
||||
VoteRequest request = new VoteRequest(handshakeContents[2], toSend);
|
||||
System.out.println(request);
|
||||
if (nuVotifier.isDebug()) {
|
||||
nuVotifier.getLogger().info("Sent request: " + request.toString());
|
||||
}
|
||||
ctx.writeAndFlush(request);
|
||||
ctx.pipeline().addLast(new VotifierProtocol2ResponseHandler(responseHandler));
|
||||
ctx.pipeline().remove(this);
|
||||
|
||||
@@ -58,8 +58,8 @@ forwarding:
|
||||
Hub:
|
||||
address: 127.0.0.1
|
||||
port: 8193
|
||||
key: MyKeyHere
|
||||
token: MyKeyHere
|
||||
Hub2:
|
||||
address: 127.0.0.1
|
||||
port: 8194
|
||||
key: AnotherKeyHere
|
||||
token: AnotherKeyHere
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public interface VoteHandler {
|
||||
void onVoteReceived(Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception;
|
||||
void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception;
|
||||
|
||||
void onError(Channel channel, Throwable throwable);
|
||||
}
|
||||
@@ -52,9 +52,6 @@ public class Vote {
|
||||
}
|
||||
|
||||
public Vote(String serviceName, String username, String address, String timeStamp) {
|
||||
if (username.length() > 16) {
|
||||
throw new IllegalArgumentException("Username is too long.");
|
||||
}
|
||||
this.serviceName = serviceName;
|
||||
this.username = username;
|
||||
this.address = address;
|
||||
@@ -105,9 +102,6 @@ public class Vote {
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUsername(String username) {
|
||||
if (username.length() > 16) {
|
||||
throw new IllegalArgumentException("Username is too long.");
|
||||
}
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public class VoteInboundHandler extends SimpleChannelInboundHandler<Vote> {
|
||||
protected void channelRead0(ChannelHandlerContext ctx, final Vote vote) throws Exception {
|
||||
VotifierSession session = ctx.channel().attr(VotifierSession.KEY).get();
|
||||
|
||||
handler.onVoteReceived(vote, session.getVersion());
|
||||
handler.onVoteReceived(ctx.channel(), vote, session.getVersion());
|
||||
|
||||
if (session.getVersion() == VotifierSession.ProtocolVersion.ONE) {
|
||||
ctx.close();
|
||||
|
||||
+6
-2
@@ -17,10 +17,14 @@ import java.util.List;
|
||||
public class VotifierProtocol1Decoder extends ByteToMessageDecoder {
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
|
||||
byte[] block = new byte[256];
|
||||
if (buf.readableBytes() < 256) {
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] block = new byte[buf.readableBytes()];
|
||||
buf.getBytes(0, block);
|
||||
// "Drain" the whole buffer
|
||||
buf.readerIndex(buf.capacity());
|
||||
buf.readerIndex(buf.readableBytes());
|
||||
|
||||
VotifierPlugin plugin = ctx.channel().attr(VotifierPlugin.KEY).get();
|
||||
|
||||
|
||||
+12
-8
@@ -12,10 +12,7 @@ import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -63,6 +60,10 @@ public class VotifierProtocol2Decoder extends MessageToMessageDecoder<String> {
|
||||
UUID.fromString(votePayload.getString("uuid"));
|
||||
}
|
||||
|
||||
if (votePayload.getString("username").length() > 16) {
|
||||
throw new CorruptedFrameException("Username too long");
|
||||
}
|
||||
|
||||
// Create the vote.
|
||||
Vote vote = new Vote(votePayload);
|
||||
list.add(vote);
|
||||
@@ -75,16 +76,19 @@ public class VotifierProtocol2Decoder extends MessageToMessageDecoder<String> {
|
||||
// This randomizes the byte order to make timing attacks more difficult.
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(key);
|
||||
byte[] messageCalc = mac.doFinal(message);
|
||||
byte[] calculatedSig = mac.doFinal(message);
|
||||
|
||||
Mac mac2 = Mac.getInstance("HmacSHA256");
|
||||
// Generate a random key for use in comparison
|
||||
byte[] randomKey = new byte[32];
|
||||
RANDOM.nextBytes(randomKey);
|
||||
|
||||
// Then generate two HMACs for the different signatures found
|
||||
Mac mac2 = Mac.getInstance("HmacSHA256");
|
||||
mac2.init(new SecretKeySpec(randomKey, "HmacSHA256"));
|
||||
byte[] clientSig = mac2.doFinal(sig);
|
||||
mac2.reset();
|
||||
byte[] realSig = mac2.doFinal(messageCalc);
|
||||
byte[] realSig = mac2.doFinal(calculatedSig);
|
||||
|
||||
return Arrays.equals(clientSig, realSig);
|
||||
return MessageDigest.isEqual(clientSig, realSig);
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -29,12 +29,10 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> list) throws Exception {
|
||||
// Determine the number of bytes that are available.
|
||||
int readable = buf.readableBytes();
|
||||
buf.retain(); // TODO: Is this needed?
|
||||
|
||||
if (readable < 2) {
|
||||
// Some retarded voting sites (PMC?) seem to send empty buffers for no good reason.
|
||||
// TODO: How can we handle this?
|
||||
throw new CorruptedFrameException("Frame is too short");
|
||||
return;
|
||||
}
|
||||
|
||||
short readMagic = buf.readShort();
|
||||
@@ -46,6 +44,8 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
|
||||
|
||||
if (readMagic == PROTOCOL_2_MAGIC) {
|
||||
// Short 0x733A + Message = Protocol v2 Vote
|
||||
session.setVersion(VotifierSession.ProtocolVersion.TWO);
|
||||
|
||||
if (!testMode) {
|
||||
ctx.pipeline().addAfter("protocolDifferentiator", "protocol2LengthDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, 0, 4));
|
||||
ctx.pipeline().addAfter("protocol2LengthDecoder", "protocol2StringDecoder", new StringDecoder(StandardCharsets.UTF_8));
|
||||
@@ -53,17 +53,16 @@ public class VotifierProtocolDifferentiator extends ByteToMessageDecoder {
|
||||
ctx.pipeline().addAfter("protocol2VoteDecoder", "protocol2StringEncoder", new StringEncoder(StandardCharsets.UTF_8));
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
session.setVersion(VotifierSession.ProtocolVersion.TWO);
|
||||
} else {
|
||||
if (!allowv1) {
|
||||
throw new CorruptedFrameException("This server only accepts well-formed Votifier v2 packets.");
|
||||
}
|
||||
// Probably Protocol v1 Vote Message
|
||||
session.setVersion(VotifierSession.ProtocolVersion.ONE);
|
||||
if (!testMode) {
|
||||
ctx.pipeline().addAfter("protocolDifferentiator", "protocol1Handler", new VotifierProtocol1Decoder());
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
session.setVersion(VotifierSession.ProtocolVersion.ONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package com.vexsoftware.votifier.net.protocol.v1crypto;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
@@ -45,21 +46,18 @@ public class RSAIO {
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
// Store the public key.
|
||||
// Store the public and private keys.
|
||||
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(
|
||||
publicKey.getEncoded());
|
||||
FileOutputStream out = new FileOutputStream(directory + "/public.key");
|
||||
out.write(DatatypeConverter.printBase64Binary(publicSpec.getEncoded())
|
||||
.getBytes());
|
||||
out.close();
|
||||
|
||||
// Store the private key.
|
||||
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(
|
||||
privateKey.getEncoded());
|
||||
out = new FileOutputStream(directory + "/private.key");
|
||||
out.write(DatatypeConverter.printBase64Binary(privateSpec.getEncoded())
|
||||
.getBytes());
|
||||
out.close();
|
||||
try (FileOutputStream publicOut = new FileOutputStream(directory + "/public.key");
|
||||
FileOutputStream privateOut = new FileOutputStream(directory + "/private.key")) {
|
||||
publicOut.write(DatatypeConverter.printBase64Binary(publicSpec.getEncoded())
|
||||
.getBytes(StandardCharsets.UTF_8));
|
||||
privateOut.write(DatatypeConverter.printBase64Binary(privateSpec.getEncoded())
|
||||
.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,13 +73,13 @@ public class RSAIO {
|
||||
File publicKeyFile = new File(directory + "/public.key");
|
||||
byte[] encodedPublicKey = Files.readAllBytes(publicKeyFile.toPath());
|
||||
encodedPublicKey = DatatypeConverter.parseBase64Binary(new String(
|
||||
encodedPublicKey));
|
||||
encodedPublicKey, StandardCharsets.UTF_8));
|
||||
|
||||
// Read the private key file.
|
||||
File privateKeyFile = new File(directory + "/private.key");
|
||||
byte[] encodedPrivateKey = Files.readAllBytes(privateKeyFile.toPath());
|
||||
encodedPrivateKey = DatatypeConverter.parseBase64Binary(new String(
|
||||
encodedPrivateKey));
|
||||
encodedPrivateKey, StandardCharsets.UTF_8));
|
||||
|
||||
// Instantiate and return the key pair.
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.vexsoftware.votifier.util.standalone;
|
||||
|
||||
import com.vexsoftware.votifier.VoteHandler;
|
||||
import com.vexsoftware.votifier.VotifierPlugin;
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import com.vexsoftware.votifier.net.protocol.VoteInboundHandler;
|
||||
import com.vexsoftware.votifier.net.protocol.VotifierGreetingHandler;
|
||||
import com.vexsoftware.votifier.net.protocol.VotifierProtocolDifferentiator;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class StandaloneVotifierPlugin implements VotifierPlugin, VoteHandler {
|
||||
private final Map<String, Key> tokens;
|
||||
private final VoteReceiver receiver;
|
||||
private final KeyPair v1Key;
|
||||
private final InetSocketAddress bind;
|
||||
private NioEventLoopGroup group;
|
||||
|
||||
public StandaloneVotifierPlugin(Map<String, Key> tokens, VoteReceiver receiver, KeyPair v1Key, InetSocketAddress bind) {
|
||||
this.receiver = receiver;
|
||||
this.bind = bind;
|
||||
this.tokens = Collections.unmodifiableMap(new HashMap<>(tokens));
|
||||
this.v1Key = v1Key;
|
||||
}
|
||||
|
||||
public Throwable start() throws Throwable {
|
||||
group = new NioEventLoopGroup(2);
|
||||
|
||||
ChannelFuture future = new ServerBootstrap()
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.group(group)
|
||||
.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(NioSocketChannel channel) throws Exception {
|
||||
channel.attr(VotifierSession.KEY).set(new VotifierSession());
|
||||
channel.attr(VotifierPlugin.KEY).set(StandaloneVotifierPlugin.this);
|
||||
channel.pipeline().addLast("greetingHandler", new VotifierGreetingHandler());
|
||||
channel.pipeline().addLast("protocolDifferentiator", new VotifierProtocolDifferentiator(false, v1Key != null));
|
||||
channel.pipeline().addLast("voteHandler", new VoteInboundHandler(StandaloneVotifierPlugin.this));
|
||||
}
|
||||
})
|
||||
.bind(bind)
|
||||
.syncUninterruptibly();
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
return future.cause();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Key> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair getProtocolV1Key() {
|
||||
return v1Key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "2.3.4";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVoteReceived(Channel channel, Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
receiver.onVote(vote);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Channel channel, Throwable throwable) {
|
||||
receiver.onException(throwable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.vexsoftware.votifier.util.standalone;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
|
||||
public interface VoteReceiver {
|
||||
void onVote(Vote vote) throws Exception;
|
||||
void onException(Throwable throwable);
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.vexsoftware.votifier.util.standalone;
|
||||
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSAIO;
|
||||
import com.vexsoftware.votifier.util.KeyCreator;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class VotifierServerBuilder {
|
||||
private final Map<String, Key> keyMap = new HashMap<>();
|
||||
private KeyPair v1Key;
|
||||
private InetSocketAddress bind;
|
||||
private VoteReceiver receiver;
|
||||
|
||||
public VotifierServerBuilder addToken(String service, String token) {
|
||||
Objects.requireNonNull(service, "service");
|
||||
Objects.requireNonNull(token, "key");
|
||||
|
||||
keyMap.put(service, KeyCreator.createKeyFrom(token));
|
||||
return this;
|
||||
}
|
||||
|
||||
public VotifierServerBuilder v1Key(KeyPair v1Key) {
|
||||
this.v1Key = Objects.requireNonNull(v1Key, "v1Key");
|
||||
if (!(v1Key.getPrivate() instanceof RSAKey)) {
|
||||
throw new IllegalArgumentException("Provided key is not an RSA key.");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public VotifierServerBuilder v1KeyFolder(File file) throws Exception {
|
||||
this.v1Key = RSAIO.load(Objects.requireNonNull(file, "file"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public VotifierServerBuilder bind(InetSocketAddress bind) {
|
||||
this.bind = Objects.requireNonNull(bind, "bind");
|
||||
return this;
|
||||
}
|
||||
|
||||
public VotifierServerBuilder receiver(VoteReceiver receiver) {
|
||||
this.receiver = Objects.requireNonNull(receiver, "receiver");
|
||||
return this;
|
||||
}
|
||||
|
||||
public StandaloneVotifierPlugin create() {
|
||||
Objects.requireNonNull(bind, "need an address to bind to");
|
||||
Objects.requireNonNull(receiver, "need a receiver for votes");
|
||||
return new StandaloneVotifierPlugin(keyMap, receiver, v1Key, bind);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.vexsoftware.votifier.net.protocol;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.net.protocol.v1crypto.RSA;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VoteUtil {
|
||||
public static byte[] encodePOJOv1(Vote vote) throws Exception {
|
||||
return encodePOJOv1(vote, TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
|
||||
}
|
||||
|
||||
public static byte[] encodePOJOv1(Vote vote, PublicKey key) throws Exception {
|
||||
List<String> ordered = new ArrayList<>();
|
||||
ordered.add("VOTE");
|
||||
ordered.add(vote.getServiceName());
|
||||
ordered.add(vote.getUsername());
|
||||
ordered.add(vote.getAddress());
|
||||
ordered.add(vote.getTimeStamp());
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : ordered) {
|
||||
builder.append(s).append('\n'); // naive join needed!
|
||||
}
|
||||
|
||||
return RSA.encrypt(builder.toString().getBytes(StandardCharsets.UTF_8), key);
|
||||
}
|
||||
}
|
||||
+2
-7
@@ -29,16 +29,12 @@ public class VotifierProtocol1DecoderTest {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulDecode() throws Exception {
|
||||
// Encode a test vote.
|
||||
String voteString = "VOTE\nTest\ntest\ntest\ntest\n";
|
||||
|
||||
// For reference, this is the same vote as a POJO:
|
||||
Vote votePojo = new Vote("Test", "test", "test", "test");
|
||||
|
||||
// Send the vote
|
||||
EmbeddedChannel channel = createChannel();
|
||||
|
||||
byte[] encrypted = RSA.encrypt(voteString.getBytes(StandardCharsets.UTF_8), TestVotifierPlugin.getI().getProtocolV1Key().getPublic());
|
||||
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
|
||||
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
|
||||
|
||||
assertTrue(channel.writeInbound(encryptedByteBuf));
|
||||
@@ -81,8 +77,7 @@ public class VotifierProtocol1DecoderTest {
|
||||
// Send the bad vote
|
||||
EmbeddedChannel channel = createChannel();
|
||||
|
||||
String voteString = "VOTE\nTest\ntest\ntest\ntest\n";
|
||||
byte[] encrypted = RSA.encrypt(voteString.getBytes(StandardCharsets.UTF_8), badPublicKey);
|
||||
byte[] encrypted = VoteUtil.encodePOJOv1(new Vote("Test", "test", "test", "test"), badPublicKey);
|
||||
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
|
||||
|
||||
try {
|
||||
|
||||
+19
-15
@@ -1,5 +1,6 @@
|
||||
package com.vexsoftware.votifier.net.protocol;
|
||||
|
||||
import com.vexsoftware.votifier.model.Vote;
|
||||
import com.vexsoftware.votifier.net.VotifierSession;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -62,21 +63,6 @@ public class VotifierProtocolDifferentiatorTest {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Test(expected = DecoderException.class)
|
||||
public void failOnSmallBufferTest() {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
|
||||
|
||||
ByteBuf buf = Unpooled.buffer(1);
|
||||
buf.writeByte(0);
|
||||
|
||||
try {
|
||||
channel.writeInbound(buf);
|
||||
} finally {
|
||||
buf.release();
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = DecoderException.class)
|
||||
public void failOnBadPacketTest() {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, false));
|
||||
@@ -93,4 +79,22 @@ public class VotifierProtocolDifferentiatorTest {
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tryIdentifyRealVotev1() throws Exception {
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new VotifierProtocolDifferentiator(true, true));
|
||||
|
||||
VotifierSession session = new VotifierSession();
|
||||
channel.attr(VotifierSession.KEY).set(session);
|
||||
|
||||
Vote votePojo = new Vote("Test", "test", "test", "test");
|
||||
byte[] encrypted = VoteUtil.encodePOJOv1(votePojo);
|
||||
ByteBuf encryptedByteBuf = Unpooled.wrappedBuffer(encrypted);
|
||||
|
||||
channel.writeInbound(encryptedByteBuf);
|
||||
|
||||
assertEquals(VotifierSession.ProtocolVersion.ONE, session.getVersion());
|
||||
encryptedByteBuf.release();
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
<modules>
|
||||
<module>common</module>
|
||||
<module>bukkit</module>
|
||||
@@ -23,8 +23,8 @@
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>bukkit</id>
|
||||
<url>http://repo.bukkit.org/content/groups/public/</url>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
+7
-2
@@ -5,12 +5,17 @@
|
||||
<parent>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>nuvotifier-sponge</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sponge-maven-repo</id>
|
||||
@@ -50,7 +55,7 @@
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>spongeapi</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.config.ConfigDir;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.cause.Cause;
|
||||
import org.spongepowered.api.event.cause.NamedCause;
|
||||
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
|
||||
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
|
||||
import org.spongepowered.api.plugin.Plugin;
|
||||
@@ -52,7 +53,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.2")
|
||||
@Plugin(id = "nuvotifier", name = "NuVotifier", version = "2.3.4")
|
||||
public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.VotifierPlugin, ForwardedVoteListener {
|
||||
private Logger logger;
|
||||
@Inject
|
||||
@@ -229,7 +230,7 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
serverChannel = future.channel();
|
||||
logger.info("Votifier enabled.");
|
||||
logger.info("Votifier enabled on socket "+serverChannel.localAddress()+".");
|
||||
} else {
|
||||
SocketAddress socketAddress = future.channel().localAddress();
|
||||
if (socketAddress == null) {
|
||||
@@ -339,19 +340,19 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVoteReceived(final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
public void onVoteReceived(Channel channel, final Vote vote, VotifierSession.ProtocolVersion protocolVersion) throws Exception {
|
||||
if (debug) {
|
||||
if (protocolVersion == VotifierSession.ProtocolVersion.ONE) {
|
||||
logger.info("Got a protocol v1 vote record -> " + vote);
|
||||
logger.info("Got a protocol v1 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
} else {
|
||||
logger.info("Got a protocol v2 vote record -> " + vote);
|
||||
logger.info("Got a protocol v2 vote record from " + channel.remoteAddress() + " -> " + vote);
|
||||
}
|
||||
}
|
||||
Sponge.getScheduler().createTaskBuilder()
|
||||
.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VotifierEvent event = new VotifierEvent(vote, Cause.of(VotifierPlugin.this));
|
||||
VotifierEvent event = new VotifierEvent(vote, Cause.of(NamedCause.of("Vote", vote)));
|
||||
Sponge.getEventManager().post(event);
|
||||
}
|
||||
})
|
||||
@@ -377,7 +378,7 @@ public class VotifierPlugin implements VoteHandler, com.vexsoftware.votifier.Vot
|
||||
.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
VotifierEvent event = new VotifierEvent(v, Cause.of(VotifierPlugin.this));
|
||||
VotifierEvent event = new VotifierEvent(v, Cause.of(NamedCause.of("ForwardedVote", v)));
|
||||
Sponge.getEventManager().post(event);
|
||||
}
|
||||
})
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>nuvotifier</artifactId>
|
||||
<groupId>com.vexsoftware</groupId>
|
||||
<version>2.3.2</version>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ networks.
|
||||
# Installation
|
||||
|
||||
The NuVotifier Universal jar (the default jar that is distributed on spigotmc.org) can be installed on Bukkit, BungeeCord,
|
||||
and Sponge servers as-is without modification.
|
||||
and Sponge servers as-is.
|
||||
|
||||
The Universal JAR can be downloaded off our [SpigotMC resource page](https://www.spigotmc.org/resources/nuvotifier.13449/).
|
||||
|
||||
@@ -18,7 +18,7 @@ the legacy protocol will be referred to v1. As of writing, most if not all vote
|
||||
|
||||
## Enabling the NuVotifier Server port
|
||||
|
||||
NuVotifier allows for the server port to be turned off when NuVotifier is only being used to forward votes. This is only
|
||||
NuVotifier allows for the server port to be turned off when NuVotifier is only receiving forwarded votes. This is only
|
||||
an option on Bukkit based servers, since it would not make logical sense for the port to be turned off on Bungee servers.
|
||||
|
||||
If you are receiving votes from a server list directly on the server in question (votes are not being forwarded through plugin
|
||||
@@ -28,10 +28,10 @@ be set to false.
|
||||
|
||||
## Port Configuration
|
||||
|
||||
One of NuVotifier's functions is acting as a vote server (receiver). The IP for NuVotifier to bind to is configured
|
||||
NuVotifier's main function is acting as a vote server (receiver). The IP for NuVotifier to bind to is configured
|
||||
by the `host` and `ip` configuration values within the config.yml files within the NuVotifier plugin folder.
|
||||
Most times, you will want this `host` value to be set to `0.0.0.0` or your server's ip. `0.0.0.0` will bind to all
|
||||
ip ports on your machine, while specifying a certain IP will only bind to that ip. If you don't know what to set this
|
||||
network addresses on your machine, while specifying a certain IP will only bind to that ip. If you don't know what to set this
|
||||
to, `0.0.0.0` is a safe bet. By default, NuVotifier listens on port 8192. This is the same as the default Votifier port.
|
||||
If you change this, you will have to remember this when setting up forwarding or server lists.
|
||||
|
||||
@@ -39,7 +39,7 @@ If you change this, you will have to remember this when setting up forwarding or
|
||||
|
||||
When first setting up NuVotifier, you will probably want debug mode to be enabled. This prints extra information to the
|
||||
console, ensuring that votes are being processed as they should. If you are experiencing an error, or having issues with
|
||||
votes being processed, you should first look to enable this. Debug mode can be turned on by setting `debug` in the config.yml
|
||||
votes being processed, you should first enable this. Debug mode can be turned on by setting `debug` in the config.yml
|
||||
to true.
|
||||
|
||||
## Tokens vs Public Key
|
||||
@@ -51,7 +51,7 @@ should be used. When setting up the `proxy` forwarding method, tokens should be
|
||||
Making a new token is as easy as adding another entry into `tokens` with the serviceName as the key and whatever string you want
|
||||
as the value. You can usually find the serviceName of a vote provider on their setup page if they use v2.
|
||||
|
||||
Unless a website specifies it, you should assume that the website is using v1, and you should enter your `rsa/public.key`.
|
||||
Unless a website specifies it, you should assume that the website is using v1, and you should use `rsa/public.key`.
|
||||
|
||||
## Forwarding
|
||||
|
||||
@@ -90,22 +90,22 @@ This is suitable for testing, but not for production system.
|
||||
|
||||
Votes can also be cached in a file located in the NuVotifier plugin folder. To enable this, you can set `cache` to file.
|
||||
If you want to change the file location from the default, you can set this under the `name` config under the `file` section.
|
||||
This caching method is suitable for most people.
|
||||
This caching method is suitable for most people. This is the default option.
|
||||
|
||||
##### No Caching
|
||||
|
||||
Caching can also be turned off. This is the default option. You can do this by setting `cache` to `none`. This is also not
|
||||
recommended, and is only suitable for a certain number of setups.
|
||||
Caching can also be turned off. You can do this by setting `cache` to `none`. This is also not recommended, and is only
|
||||
suitable for a certain number of setups.
|
||||
|
||||
### Proxy Based Forwarding
|
||||
|
||||
You can also forward votes using the proxy method. This will cause the BungeeCord to act as a voting websites, sending votes
|
||||
You can also forward votes using the proxy method. This will cause the BungeeCord to act like a voting website, sending votes
|
||||
to each server's NuVotifier port. To enable this method, you must set the `method` under `forwarding` to `proxy` in your
|
||||
BungeeCord config.yml. Within the server's config.yml, `forwarding` should be set to `none`.
|
||||
|
||||
For each server you wish to forward to, you must add each to your BungeeCord config.yml under the `proxy` section of `forwarding`.
|
||||
For each, you must specify the ip address (`address`), port (`port`) and token (`key`). Each backend Bukkit server should have
|
||||
1 default token under the `tokens` section. This is the token that should be used as the token for the `key` in that server's
|
||||
For each, you must specify the ip address (`address`), port (`port`) and token (`token`). Each backend Bukkit server should have
|
||||
1 default token under the `tokens` section. This is the token that should be used as the token for the `token` in that server's
|
||||
section within the `proxy` section. If the server is on the same server (same ip) as the BungeeCord server, you may
|
||||
use the `127.0.0.1` or `localhost` address, setting both the `proxy` forwarding address and Bukkit config.yml address to it.
|
||||
|
||||
|
||||
Reference in new issue
Block a user