瀏覽代碼

Untested Client

Billy Barrow 2 年之前
父節點
當前提交
af0d502195
共有 7 個文件被更改,包括 271 次插入4 次删除
  1. 124 0
      src/lib/Client.vala
  2. 20 0
      src/lib/KnownHostDiscoverer.vala
  3. 83 0
      src/lib/LanDiscoverer.vala
  4. 17 2
      src/lib/Message.vala
  5. 22 0
      src/lib/PeerDiscoverer.vala
  6. 2 2
      src/lib/Server.vala
  7. 3 0
      src/lib/meson.build

+ 124 - 0
src/lib/Client.vala

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

+ 20 - 0
src/lib/KnownHostDiscoverer.vala

@@ -0,0 +1,20 @@
+namespace Riddle {
+
+    public class KnownHostDiscoverer : PeerDiscoverer {
+
+        private Invercargill.Sequence<InetSocketAddress> peers;
+
+        public KnownHostDiscoverer() {
+            // TODO read from config
+            peers = new Invercargill.Sequence<InetSocketAddress> ();
+        }
+
+        protected override void discover_peers () {
+            foreach (var peer in peers) {
+                peer_discovered (peer);
+            }
+        }
+
+    }
+
+}

+ 83 - 0
src/lib/LanDiscoverer.vala

@@ -0,0 +1,83 @@
+namespace Riddle {
+
+    public class LanDiscoverer : PeerDiscoverer {
+
+        private InetSocketAddress? server_address;
+        private InetSocketAddress multicast_address;
+        private Socket? multicast_socket = null;
+
+        public LanDiscoverer() {
+            multicast_address = new InetSocketAddress(new InetAddress.from_string("224.0.0.2"), 1997);
+        }
+
+        protected override void discover_peers () {
+            try {
+                lock(multicast_socket) {
+                    multicast_socket = new Socket(SocketFamily.IPV4, SocketType.DATAGRAM, SocketProtocol.UDP);
+                    multicast_socket.bind(multicast_address, true);
+                    multicast_socket.join_multicast_group(multicast_address.get_address(), false, null);
+                    if(server_address != null) {
+                        send_advertisement();
+                    }
+                }
+                query_peers();
+            }
+            catch(Error e) {
+                warning(@"Error starting peer discoverer for local area network: $(e.message)");
+                return;
+            }
+            while (true) {
+                try {
+                    var buffer = new uint8[2048];
+                    buffer.length = (int)multicast_socket.receive(buffer);
+
+                    var message = (string)buffer;
+                    if(!message.has_prefix ("Riddle ")) {
+                        // Spam, skip
+                        continue;
+                    }
+
+                    var parts = message.split(" ", 4);
+                    if(parts[1] == "PA") {
+                        var add_str = parts[2];
+                        var port = uint.parse(parts[3]);
+                        var address = new InetSocketAddress.from_string(add_str, port);
+                        peer_discovered(address);
+                        continue;
+                    }
+                    if(parts[1] == "PQ" && server_address != null) {
+                        // Random cooldown before sending advertisment
+                        Thread.usleep(Random.int_range (10, 1000) * 1000);
+                        send_advertisement();
+                    }
+                }
+                catch(Error e) {
+                    warning(@"Error in lan discovery thread: $(e.message)");
+                    info("5 second cooldown before attempting socket recieve again.");
+                    Thread.usleep (5 * 1000 * 1000);
+                }
+            }
+        }
+
+        public void query_peers() throws Error {
+            var query = "Riddle PQ".data;
+            multicast_socket.send_to (multicast_address, query);
+        }
+
+        public void advertise(InetSocketAddress address) throws Error {
+            lock(multicast_socket) {
+                server_address = address;
+                if(multicast_socket != null) {
+                    send_advertisement();
+                }
+            }
+        }
+
+        private void send_advertisement() throws Error {
+            var advertisement = @"Riddle PA $(server_address.address) $(server_address.port)".data;
+            multicast_socket.send_to (multicast_address, advertisement);
+        }
+
+    }
+
+}

+ 17 - 2
src/lib/Message.vala

@@ -128,7 +128,7 @@ namespace Riddle {
             return @"$type_string $args\n$data";
         }
 
-        public static async Message from_stream(DataInputStream stream) throws Error {
+        public static async Message from_stream_async(DataInputStream stream) throws Error {
             var lines = new Invercargill.Sequence<string>();
             var last_line = "";
             while(true) {
@@ -142,7 +142,22 @@ namespace Riddle {
             return new Message.from_lines(lines.to_array());
         }
 
-        public async void send(DataOutputStream stream) throws Error {
+
+        public static Message from_stream(DataInputStream stream) throws Error {
+            var lines = new Invercargill.Sequence<string>();
+            var last_line = "";
+            while(true) {
+                last_line = stream.read_line();
+                if(last_line == "") {
+                    break;
+                }
+                lines.add(last_line);
+            }
+
+            return new Message.from_lines(lines.to_array());
+        }
+
+        public void send(DataOutputStream stream) throws Error {
             stream.put_string(@"$this\n\n");
         }
     }

+ 22 - 0
src/lib/PeerDiscoverer.vala

@@ -0,0 +1,22 @@
+namespace Riddle {
+
+    public abstract class PeerDiscoverer {
+
+        protected abstract void discover_peers();
+        public signal void peer_discovered(InetSocketAddress address);
+        private Thread<bool> discovery_thread = null;
+
+        public virtual void begin() {
+            if(discovery_thread == null) {
+                discovery_thread = new Thread<bool>("Riddle Peer Discovery Thread", begin_discovery);
+            }
+        }
+
+        private bool begin_discovery() {
+            discover_peers();
+            return false;
+        }
+
+    }
+
+}

+ 2 - 2
src/lib/Server.vala

@@ -27,10 +27,10 @@ namespace Riddle {
                     var dis = new DataInputStream(connection.input_stream);
                     var dos = new DataOutputStream(connection.output_stream);
 
-                    var message = yield Message.from_stream(dis);
+                    var message = yield Message.from_stream_async(dis);
                     print(@"Received $(MessageType.to_string(message.message_type)) message\n");
                     var reply = service_message(message, (InetSocketAddress)connection.get_remote_address());
-                    yield reply.send(dos);
+                    reply.send(dos);
                     yield dos.flush_async();
                     yield connection.close_async();
                 }

+ 3 - 0
src/lib/meson.build

@@ -27,6 +27,9 @@ sources += files('Client.vala')
 sources += files('NameInfo.vala')
 sources += files('CertifiedNameInfo.vala')
 sources += files('DecentralisedNameInfo.vala')
+sources += files('PeerDiscoverer.vala')
+sources += files('KnownHostDiscoverer.vala')
+sources += files('LanDiscoverer.vala')