|
@@ -4,6 +4,130 @@ namespace Riddle {
|
|
|
|
|
|
public class Client {
|
|
|
|
|
|
+ private Invercargill.Sequence<PeerDiscoverer> discoverers;
|
|
|
+ private Invercargill.Sequence<InetSocketAddress> servers;
|
|
|
+ private InetSocketAddress? self_server;
|
|
|
+ private SocketClient socket_client;
|
|
|
+
|
|
|
+ public Client() {
|
|
|
+ socket_client = new SocketClient();
|
|
|
+ discoverers = new Invercargill.Sequence<PeerDiscoverer> ();
|
|
|
+ discoverers.add (new KnownHostDiscoverer());
|
|
|
+ discoverers.add (new LanDiscoverer());
|
|
|
+
|
|
|
+ foreach (var discoverer in discoverers) {
|
|
|
+ discoverer.peer_discovered.connect(new_server_found);
|
|
|
+ discoverer.begin();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void new_server_found(InetSocketAddress address) {
|
|
|
+ if(self_server != null && server_equals(address, self_server)) {
|
|
|
+ // Ignore self_server address
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(servers.any(s => server_equals(s, address))) {
|
|
|
+ // Ignore already discovered server
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ servers.add(address);
|
|
|
+ }
|
|
|
+
|
|
|
+ public int join(string group, uint port) {
|
|
|
+ var msg = new Message(MessageType.JOIN, new string[] { group, port.to_string() }, new string[0]);
|
|
|
+ var responses = send_message_to_servers(msg);
|
|
|
+ return responses.where(r => r.message_type == MessageType.OK).count();
|
|
|
+ }
|
|
|
+
|
|
|
+ public int leave(string group, uint port) {
|
|
|
+ var msg = new Message(MessageType.LEAVE, new string[] { group, port.to_string() }, new string[0]);
|
|
|
+ var responses = send_message_to_servers(msg);
|
|
|
+ return responses.where(r => r.message_type == MessageType.OK).count();
|
|
|
+ }
|
|
|
+
|
|
|
+ public int propogate(NameInfo info) {
|
|
|
+ var msg = new Message(MessageType.PROPOGATE, new string[] { info.name, "10" }, new string[] { info.get_encoded() });
|
|
|
+ var responses = send_message_to_servers(msg);
|
|
|
+ return responses.where(r => r.message_type == MessageType.OK).count();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Enumerable<InetSocketAddress> who_is_in(string group) {
|
|
|
+ var who_in = new Message(MessageType.WHO_IN, new string[] { group }, new string[0]);
|
|
|
+ var responses = send_message_to_servers(who_in);
|
|
|
+ return responses.select_many<InetSocketAddress>(r => ate(r.items).select<InetSocketAddress>(i => parse_address(i)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public Enumerable<NameInfo> who_is(string domain) {
|
|
|
+ var who_is = new Message(MessageType.WHO_IS, new string[] { domain }, new string[0]);
|
|
|
+ var responses = send_message_to_servers(who_is);
|
|
|
+ if(domain.has_suffix(".rns")) {
|
|
|
+ return responses.select_many<NameInfo>(r => ate(r.items).select<NameInfo>(i => new DecentralisedNameInfo.from_string(i)));
|
|
|
+ }
|
|
|
+
|
|
|
+ return responses.select_many<NameInfo>(r => ate(r.items).select<NameInfo>(i => new CertifiedNameInfo.from_string(i)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public int riddle(RiddleEnvelope riddle) {
|
|
|
+ var msg = riddle.to_message();
|
|
|
+ var responses = send_message_to_servers(msg);
|
|
|
+ return responses.where(r => r.message_type == MessageType.OK).count();
|
|
|
+ }
|
|
|
+
|
|
|
+ public InetSocketAddress? callback(SolutionEnvelope solution, InetSocketAddress server, uint8[] author_signing_key, uint8[] reply_public_key, uint8[] reply_secret_key) throws Error {
|
|
|
+ var socket = socket_client.connect(server);
|
|
|
+ var dis = new DataInputStream(socket.input_stream);
|
|
|
+ var dos = new DataOutputStream(socket.output_stream);
|
|
|
+
|
|
|
+ solution.to_message().send(dos);
|
|
|
+ var reply = Message.from_stream(dis);
|
|
|
+
|
|
|
+ if(reply.message_type == MessageType.NOT_ACCEPTED) {
|
|
|
+ warning(@"Got NOT-ACCEPTED response from $(server.address): $(reply.arguments[0]) $(reply.arguments[1])");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var verified_reply = Solution.verify_solved_response(reply, author_signing_key);
|
|
|
+ if(verified_reply == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Solution.decrypt_connection_details(verified_reply, reply_public_key, reply_secret_key);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool server_equals(InetSocketAddress a1, InetSocketAddress a2) {
|
|
|
+ return (a1.address.equal(a2.address) && a1.port == a2.port);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Invercargill.Sequence<Message> send_message_to_servers(Message message) {
|
|
|
+ return servers.parallel_select<Message?>(server => {
|
|
|
+ try {
|
|
|
+ var socket = socket_client.connect(server);
|
|
|
+ var dis = new DataInputStream(socket.input_stream);
|
|
|
+ var dos = new DataOutputStream(socket.output_stream);
|
|
|
+
|
|
|
+ message.send(dos);
|
|
|
+ var reply = Message.from_stream(dis);
|
|
|
+
|
|
|
+ if(reply.message_type == MessageType.NOT_ACCEPTED) {
|
|
|
+ warning(@"Got NOT-ACCEPTED response from $(server.address): $(reply.arguments[0]) $(reply.arguments[1])");
|
|
|
+ }
|
|
|
+ return reply;
|
|
|
+ }
|
|
|
+ catch(Error e) {
|
|
|
+ warning(@"Client message send failed: $(e.message)");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }).where(m => m != null).to_sequence();
|
|
|
+ }
|
|
|
+
|
|
|
+ private InetSocketAddress parse_address(string address) {
|
|
|
+ var parts = address.split(" ", 3);
|
|
|
+ var isa = new InetSocketAddress.from_string(parts[0], uint.parse(parts[1]));
|
|
|
+ return isa;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
}
|