Server POW and some admin functions
(needs some testing)
This commit is contained in:
parent
7a5dc1af4d
commit
5c4b976417
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
|
||||||
|
13
src/main/java/ch/dissem/bitmessage/server/Constants.java
Normal file
13
src/main/java/ch/dissem/bitmessage/server/Constants.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ch.dissem.bitmessage.server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by chrigu on 22.11.15.
|
||||||
|
*/
|
||||||
|
public interface Constants {
|
||||||
|
String ADMIN_LIST = "admins.conf";
|
||||||
|
String CLIENT_LIST = "clients.conf";
|
||||||
|
|
||||||
|
String WHITELIST = "whitelist.conf";
|
||||||
|
String SHORTLIST = "shortlist.conf";
|
||||||
|
String BLACKLIST = "blacklist.conf";
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package ch.dissem.bitmessage.server;
|
package ch.dissem.bitmessage.server;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
||||||
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
||||||
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
|
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
|
||||||
@ -16,6 +17,9 @@ import springfox.documentation.spring.web.plugins.Docket;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.server.Constants.*;
|
||||||
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class JabitServerConfig {
|
public class JabitServerConfig {
|
||||||
public static final int SHORTLIST_SIZE = 5;
|
public static final int SHORTLIST_SIZE = 5;
|
||||||
@ -36,19 +40,34 @@ public class JabitServerConfig {
|
|||||||
.messageRepo(new JdbcMessageRepository(config))
|
.messageRepo(new JdbcMessageRepository(config))
|
||||||
.nodeRegistry(new MemoryNodeRegistry())
|
.nodeRegistry(new MemoryNodeRegistry())
|
||||||
.networkHandler(new DefaultNetworkHandler())
|
.networkHandler(new DefaultNetworkHandler())
|
||||||
|
.objectListener(new ServerObjectListener(admins(), clients(), whitelist(), shortlist(), blacklist()))
|
||||||
.security(new BouncySecurity())
|
.security(new BouncySecurity())
|
||||||
.port(port)
|
.port(port)
|
||||||
.connectionLimit(connectionLimit)
|
.connectionLimit(connectionLimit)
|
||||||
.connectionTTL(connectionTTL)
|
.connectionTTL(connectionTTL)
|
||||||
.listener(plaintext -> {
|
|
||||||
})
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Set<BitmessageAddress> admins() {
|
||||||
|
return Utils.readOrCreateList(
|
||||||
|
ADMIN_LIST,
|
||||||
|
"# Admins can send commands to the server.\n"
|
||||||
|
).stream().map(BitmessageAddress::new).collect(toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Set<BitmessageAddress> clients() {
|
||||||
|
return Utils.readOrCreateList(
|
||||||
|
CLIENT_LIST,
|
||||||
|
"# Clients may send incomplete objects for proof of work.\n"
|
||||||
|
).stream().map(BitmessageAddress::new).collect(toSet());
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Set<String> whitelist() {
|
public Set<String> whitelist() {
|
||||||
return Utils.readOrCreateList(
|
return Utils.readOrCreateList(
|
||||||
"whitelist.conf",
|
WHITELIST,
|
||||||
"# If there are any Bitmessage addresses in the whitelist, only those will be shown.\n" +
|
"# If there are any Bitmessage addresses in the whitelist, only those will be shown.\n" +
|
||||||
"# blacklist.conf will be ignored, but shortlist.conf will be applied to whitelisted addresses.\n"
|
"# blacklist.conf will be ignored, but shortlist.conf will be applied to whitelisted addresses.\n"
|
||||||
);
|
);
|
||||||
@ -57,7 +76,7 @@ public class JabitServerConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public Set<String> shortlist() {
|
public Set<String> shortlist() {
|
||||||
return Utils.readOrCreateList(
|
return Utils.readOrCreateList(
|
||||||
"shortlist.conf",
|
SHORTLIST,
|
||||||
"# Broadcasts of these addresses will be restricted to the last " + SHORTLIST_SIZE + " entries.\n\n" +
|
"# Broadcasts of these addresses will be restricted to the last " + SHORTLIST_SIZE + " entries.\n\n" +
|
||||||
"# Time Service:\n" +
|
"# Time Service:\n" +
|
||||||
"BM-BcbRqcFFSQUUmXFKsPJgVQPSiFA3Xash\n\n" +
|
"BM-BcbRqcFFSQUUmXFKsPJgVQPSiFA3Xash\n\n" +
|
||||||
@ -69,7 +88,7 @@ public class JabitServerConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public Set<String> blacklist() {
|
public Set<String> blacklist() {
|
||||||
return Utils.readOrCreateList(
|
return Utils.readOrCreateList(
|
||||||
"blacklist.conf",
|
BLACKLIST,
|
||||||
"# Bitmessage addresses in this file are being ignored and their broadcasts won't be returned.\n"
|
"# Bitmessage addresses in this file are being ignored and their broadcasts won't be returned.\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,160 @@
|
|||||||
|
package ch.dissem.bitmessage.server;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.DefaultObjectListener;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
||||||
|
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.factory.Factory.getObjectMessage;
|
||||||
|
import static ch.dissem.bitmessage.server.Constants.*;
|
||||||
|
import static ch.dissem.bitmessage.server.Utils.zero;
|
||||||
|
import static ch.dissem.bitmessage.utils.Singleton.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Basler
|
||||||
|
*/
|
||||||
|
public class ServerObjectListener extends DefaultObjectListener {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ServerObjectListener.class);
|
||||||
|
|
||||||
|
private final Collection<BitmessageAddress> admins;
|
||||||
|
private final Collection<BitmessageAddress> clients;
|
||||||
|
|
||||||
|
private final Collection<String> whitelist;
|
||||||
|
private final Collection<String> shortlist;
|
||||||
|
private final Collection<String> blacklist;
|
||||||
|
|
||||||
|
public ServerObjectListener(Collection<BitmessageAddress> admins, Collection<BitmessageAddress> clients, Collection<String> whitelist, Collection<String> shortlist, Collection<String> blacklist) {
|
||||||
|
super(p -> {
|
||||||
|
});
|
||||||
|
this.admins = admins;
|
||||||
|
this.clients = clients;
|
||||||
|
this.whitelist = whitelist;
|
||||||
|
this.shortlist = shortlist;
|
||||||
|
this.blacklist = blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void receive(ObjectMessage object, Broadcast broadcast) throws IOException {
|
||||||
|
processCommands(broadcast);
|
||||||
|
if (zero(object.getNonce())) {
|
||||||
|
calculateNonceForClient(object, broadcast);
|
||||||
|
} else {
|
||||||
|
super.receive(object, broadcast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCommands(Broadcast broadcast) throws IOException {
|
||||||
|
for (BitmessageAddress admin : admins) {
|
||||||
|
try {
|
||||||
|
broadcast.decrypt(admin);
|
||||||
|
Plaintext message = broadcast.getPlaintext();
|
||||||
|
String[] command = message.getSubject().trim().toLowerCase().split("\\s+");
|
||||||
|
String data = message.getText();
|
||||||
|
if (command.length == 2) {
|
||||||
|
switch (command[1]) {
|
||||||
|
case "client":
|
||||||
|
case "clients":
|
||||||
|
updateUserList(CLIENT_LIST, clients, command[0], data);
|
||||||
|
break;
|
||||||
|
case "admin":
|
||||||
|
case "admins":
|
||||||
|
case "administrator":
|
||||||
|
case "administrators":
|
||||||
|
updateUserList(ADMIN_LIST, admins, command[0], data);
|
||||||
|
break;
|
||||||
|
case "whitelist":
|
||||||
|
updateList(WHITELIST, whitelist, command[0], data);
|
||||||
|
break;
|
||||||
|
case "shortlist":
|
||||||
|
updateList(WHITELIST, shortlist, command[0], data);
|
||||||
|
break;
|
||||||
|
case "blacklist":
|
||||||
|
updateList(WHITELIST, blacklist, command[0], data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.trace("ignoring unknown command " + message.getSubject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (DecryptionFailedException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateNonceForClient(ObjectMessage object, Broadcast broadcast) throws IOException {
|
||||||
|
for (BitmessageAddress client : clients) {
|
||||||
|
try {
|
||||||
|
broadcast.decrypt(client);
|
||||||
|
byte[] message = broadcast.getPlaintext().getMessage();
|
||||||
|
final ObjectMessage toRelay = getObjectMessage(3, new ByteArrayInputStream(message), message.length);
|
||||||
|
security().doProofOfWork(toRelay,
|
||||||
|
ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes(),
|
||||||
|
(nonce) -> {
|
||||||
|
toRelay.setNonce(nonce);
|
||||||
|
ctx.getInventory().storeObject(object);
|
||||||
|
ctx.getNetworkHandler().offer(object.getInventoryVector());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (DecryptionFailedException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateUserList(String file, Collection<BitmessageAddress> list, String command, String data) {
|
||||||
|
switch (command) {
|
||||||
|
case "set":
|
||||||
|
list.clear();
|
||||||
|
case "add":
|
||||||
|
Scanner scanner = new Scanner(data);
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
String line = scanner.nextLine();
|
||||||
|
try {
|
||||||
|
list.add(new BitmessageAddress(line));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.info(command + " " + file + ": ignoring line: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Utils.saveList(file, list.stream().map(BitmessageAddress::getAddress));
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
list.removeIf(address -> data.contains(address.getAddress()));
|
||||||
|
Utils.saveList(file, list.stream().map(BitmessageAddress::getAddress));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.info("unknown command " + command + " on list " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateList(String file, Collection<String> list, String command, String data) {
|
||||||
|
switch (command) {
|
||||||
|
case "set":
|
||||||
|
list.clear();
|
||||||
|
case "add":
|
||||||
|
Scanner scanner = new Scanner(data);
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
String line = scanner.nextLine();
|
||||||
|
try {
|
||||||
|
list.add(new BitmessageAddress(line).getAddress());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.info(command + " " + file + ": ignoring line: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Utils.saveList(file, list.stream());
|
||||||
|
break;
|
||||||
|
case "remove":
|
||||||
|
list.removeIf(data::contains);
|
||||||
|
Utils.saveList(file, list.stream());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.info("unknown command " + command + " on list " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,10 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.HashSet;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static Set<String> readOrCreateList(String filename, String content) {
|
public static Set<String> readOrCreateList(String filename, String content) {
|
||||||
@ -23,6 +26,24 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void saveList(String filename, Stream<String> content) {
|
||||||
|
try {
|
||||||
|
File file = new File(filename);
|
||||||
|
try (FileWriter fw = new FileWriter(file)) {
|
||||||
|
content.forEach(l -> {
|
||||||
|
try {
|
||||||
|
fw.write(l);
|
||||||
|
fw.write(System.lineSeparator());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Set<String> readList(File file) {
|
public static Set<String> readList(File file) {
|
||||||
Set<String> result = new HashSet<>();
|
Set<String> result = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
@ -38,4 +59,11 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean zero(byte[] nonce) {
|
||||||
|
for (byte b : nonce) {
|
||||||
|
if (b != 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user