Server POW and some admin functions

(needs some testing)
This commit is contained in:
Christian Basler 2015-11-22 12:31:51 +01:00
parent 7a5dc1af4d
commit 5c4b976417
5 changed files with 227 additions and 7 deletions

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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

View 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";
}

View File

@ -1,6 +1,7 @@
package ch.dissem.bitmessage.server;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
@ -16,6 +17,9 @@ import springfox.documentation.spring.web.plugins.Docket;
import java.util.Set;
import static ch.dissem.bitmessage.server.Constants.*;
import static java.util.stream.Collectors.toSet;
@Configuration
public class JabitServerConfig {
public static final int SHORTLIST_SIZE = 5;
@ -36,19 +40,34 @@ public class JabitServerConfig {
.messageRepo(new JdbcMessageRepository(config))
.nodeRegistry(new MemoryNodeRegistry())
.networkHandler(new DefaultNetworkHandler())
.objectListener(new ServerObjectListener(admins(), clients(), whitelist(), shortlist(), blacklist()))
.security(new BouncySecurity())
.port(port)
.connectionLimit(connectionLimit)
.connectionTTL(connectionTTL)
.listener(plaintext -> {
})
.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
public Set<String> whitelist() {
return Utils.readOrCreateList(
"whitelist.conf",
WHITELIST,
"# 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"
);
@ -57,7 +76,7 @@ public class JabitServerConfig {
@Bean
public Set<String> shortlist() {
return Utils.readOrCreateList(
"shortlist.conf",
SHORTLIST,
"# Broadcasts of these addresses will be restricted to the last " + SHORTLIST_SIZE + " entries.\n\n" +
"# Time Service:\n" +
"BM-BcbRqcFFSQUUmXFKsPJgVQPSiFA3Xash\n\n" +
@ -69,7 +88,7 @@ public class JabitServerConfig {
@Bean
public Set<String> blacklist() {
return Utils.readOrCreateList(
"blacklist.conf",
BLACKLIST,
"# Bitmessage addresses in this file are being ignored and their broadcasts won't be returned.\n"
);
}

View File

@ -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);
}
}
}

View File

@ -4,7 +4,10 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
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 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) {
Set<String> result = new HashSet<>();
try {
@ -38,4 +59,11 @@ public class Utils {
}
return result;
}
public static boolean zero(byte[] nonce) {
for (byte b : nonce) {
if (b != 0) return false;
}
return true;
}
}