Billy Barrow 1 rok pred
rodič
commit
d144a4e52f

+ 16 - 0
.vscode/launch.json

@@ -0,0 +1,16 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Debug",
+            "type": "gdb",
+            "request": "launch",
+            "target": "./build/sample/sample-server",
+            "cwd": "${workspaceRoot}",
+            "valuesFormatting": "parseText"
+        }
+    ]
+}

+ 39 - 0
src/infra/daemon/Daemon.vala

@@ -0,0 +1,39 @@
+using Riddle;
+
+namespace RiddleDaemon {
+
+    private static RiddleDaemonDBusService dbus_service;
+    
+    public static int main(string[] args) {
+    
+        if(args.length != 4) {
+            printerr("Please specify address, port, and store location only\n");
+            return -1;
+        }
+    
+        var store = new FilesystemNameInfoStore(args[3]);
+        var server = new Server(args[1], (uint16)int.parse(args[2]), store);
+        dbus_service = new RiddleDaemonDBusService (args[1], (uint16)int.parse(args[2]), server.client);
+        server.riddle_received.connect(riddle_received);
+    
+    
+        var loop = new MainLoop();
+        Bus.own_name (BusType.SESSION, "nz.astrologue.RiddleService", BusNameOwnerFlags.NONE, on_bus_aquired, () => {}, () => stderr.printf ("Could not aquire name\n"));
+        server.start.begin();
+        loop.run();
+    
+        return 0;
+    }
+    
+    private static void on_bus_aquired (DBusConnection conn) {
+        try {
+            conn.register_object ("/nz/astrologue/RiddleService", dbus_service);
+        } catch (IOError e) {
+            stderr.printf ("Could not register service\n");
+        }
+    }
+
+    private static void riddle_received(RiddleEnvelope riddle) {
+        dbus_service.received_riddle(riddle.to_message().to_string().split("\n"), riddle.reply_address.to_string());
+    }
+}

+ 42 - 0
src/infra/daemon/Service.vala

@@ -0,0 +1,42 @@
+using Riddle;
+
+namespace RiddleDaemon {
+
+    [DBus(name = "nz.astrolgoue.RiddleService")]
+    public class RiddleDaemonDBusService {
+
+        private uint16 server_port;
+        private string server_address;
+        private Client client;
+
+        public RiddleDaemonDBusService(string address, uint16 port, Client client) {
+            this.server_port = port;
+            this.server_address = address;
+            this.client = client;
+        }
+
+        public void join_group (string name) throws Error {
+            client.join (name, server_port);
+        }
+        public void leave_group (string name) throws Error {
+            // TODO: Make sure no one else still needs this since it effects the whole system
+            client.leave (name, server_port);
+        }
+        public string local_server_address() throws Error {
+            return server_address;
+        }
+        public uint16 local_server_port() throws Error {
+            return server_port;
+        }
+        public string[] discovered_servers () throws Error {
+            return client.discovered_servers.select<string>(p => @"$(p.address.to_string()) $(p.port)").to_array();
+        }
+
+        public bool is_peer_local (string address) throws Error {
+            return client.is_peer_local (new InetAddress.from_string (address));
+        }
+
+        public signal void received_riddle(string[] riddle_message_lines, string origin_address);
+    }
+
+}

+ 4 - 0
src/infra/daemon/meson.build

@@ -0,0 +1,4 @@
+sources = files('Daemon.vala')
+sources += files('Service.vala')
+
+executable('riddled', sources, dependencies: dependencies)

+ 2 - 1
src/infra/meson.build

@@ -1,2 +1,3 @@
 subdir('server')
-subdir('gen-domain')
+subdir('gen-domain')
+subdir('daemon')

+ 97 - 0
src/lib/CallbackServer.vala

@@ -0,0 +1,97 @@
+namespace Riddle {
+
+    public sealed class CallbackServer {
+
+        public InetSocketAddress socket_address { get; private set; }
+
+        private SocketService service;
+        private Gee.HashMap<string, RiddleInfo> riddles = new Gee.HashMap<string, RiddleInfo>();
+        
+        public CallbackServer(string address = "::1") throws Error {
+            var add = new InetAddress.from_string(address);
+            service = new SocketService ();
+            SocketAddress effective;
+            service.add_address(new InetSocketAddress(add, 0), SocketType.STREAM, SocketProtocol.TCP, null, out effective);
+            socket_address = (InetSocketAddress)effective;
+        }
+
+        public async void start() {
+            service.incoming.connect((c, o) => {
+                handle_connection.begin(c);
+                return false;
+            });
+            service.start ();
+        }
+
+        private async void handle_connection(SocketConnection connection) {
+            try {
+                var dis = new DataInputStream(connection.input_stream);
+                var dos = new DataOutputStream(connection.output_stream);
+
+                var message = yield Message.from_stream_async(dis);
+                var reply = service_message(message);
+                reply.send(dos);
+                yield dos.flush_async();
+                yield connection.close_async();
+            }
+            catch(Error e) {
+                warning(@"Error servicing connection: $(e.message)");
+                try{ 
+                    yield connection.close_async();
+                }
+                catch(Error e2) {
+                    warning(@"Error closing connection after initial error: $(e2.message)");
+                }
+            }
+        }
+
+
+        private Message service_message(Message msg) throws Error {
+            if(msg.message_type != MessageType.CALLBACK) {
+                return new Message(MessageType.ERROR, new string[] { "is-callback-server" }, new string[0]);
+            }
+
+            var solution_envelope = new SolutionEnvelope.from_message(msg);
+            var riddle = get_riddle(solution_envelope.identifier);
+            if(riddle == null) {
+                return new Message(MessageType.NOT_ACCEPTED, new string[] { "105", "riddle-not-found" }, new string[0]);
+            }
+
+            if(!solution_envelope.verify(riddle.envelope.solution_verification_key)) {
+                return new Message(MessageType.NOT_ACCEPTED, new string[] { "101", "verification-failed" }, new string[0]);
+            }
+
+            if(riddle.envelope.reply_address != null) {
+                return new Message(MessageType.NOT_ACCEPTED, new string[] { "106", "is-leaf-node" }, new string[0]);
+            }
+
+            return riddle.instance.mark_solution(solution_envelope);
+        }
+
+        private RiddleInfo? get_riddle(string identifier) {
+            lock(riddles) {
+                if(riddles.has_key(identifier)) {
+                    return riddles[identifier];
+                }
+            }
+            return null;
+        }
+
+        public void add_riddle(Riddle riddle, RiddleEnvelope riddle_envelope) {
+            lock(riddles) {
+                riddles.set(riddle_envelope.identifier, new RiddleInfo(riddle, riddle_envelope));
+            }
+        }
+    }
+
+    private class RiddleInfo {
+        public Riddle instance { get; private set; }
+        public RiddleEnvelope envelope { get; private set; }
+
+        public RiddleInfo(Riddle riddle, RiddleEnvelope envelope) {
+            this.instance = riddle;
+            this.envelope = envelope;
+        }
+    }
+
+}

+ 15 - 15
src/lib/CertifiedNameInfo.vala

@@ -34,27 +34,27 @@ namespace Riddle {
             }
         }
 
-        public CertifiedNameInfo(string domain, PrivateKey key, Certificate cert, Certificate[] ca_chain, Enumerable<NameProperty> properties) throws Error {
+        //  public CertifiedNameInfo(string domain, PrivateKey key, Certificate cert, Certificate[] ca_chain, Enumerable<NameProperty> properties) throws Error {
 
-            name = domain;
-            effective = new DateTime.now_utc();
-            expires = new DateTime.from_unix_utc(cert.get_expiration_time());
-            this.properties = properties;
+        //      name = domain;
+        //      effective = new DateTime.now_utc();
+        //      expires = new DateTime.from_unix_utc(cert.get_expiration_time());
+        //      this.properties = properties;
 
-            var base_info = stringify_base_info();
-            var base_data = as_datum(base_info.data);
-            var signature = new uint8[5120];
-            size_t sig_size = signature.length;
+        //      var base_info = stringify_base_info();
+        //      var base_data = as_datum(base_info.data);
+        //      var signature = new uint8[5120];
+        //      size_t sig_size = signature.length;
 
-            check_result(key.sign_data(GnuTLS.DigestAlgorithm.SHA512, 0, ref base_data, signature, ref sig_size));
+        //      check_result(key.sign_data(GnuTLS.DigestAlgorithm.SHA512, 0, ref base_data, signature, ref sig_size));
 
-            signature.length = (int)sig_size;
+        //      signature.length = (int)sig_size;
 
-            this.signature = signature;
-            this.data = base_info.data;
+        //      this.signature = signature;
+        //      this.data = base_info.data;
 
-            raw_data = serialise_data(cert, ca_chain);
-        }
+        //      raw_data = serialise_data(cert, ca_chain);
+        //  }
 
         private static GnuTLS.Datum as_datum(uint8[] data) {
             return GnuTLS.Datum() {

+ 48 - 2
src/lib/Client.vala

@@ -6,9 +6,14 @@ namespace Riddle {
 
         private Invercargill.Sequence<PeerDiscoverer> discoverers = new Invercargill.Sequence<PeerDiscoverer> ();
         private Invercargill.Sequence<InetSocketAddress> servers = new Invercargill.Sequence<InetSocketAddress>();
+        private Invercargill.Sequence<InetSocketAddress> local_servers = new Invercargill.Sequence<InetSocketAddress>();
         private Invercargill.Sequence<Message> join_messages = new Invercargill.Sequence<Message>();
         private InetSocketAddress? self_server = null;
 
+        public Invercargill.Enumerable<InetSocketAddress> discovered_servers { get {
+            return servers;
+        }}
+
         public Client() {
             discoverers.add (new KnownHostDiscoverer());
             discoverers.add (new LanDiscoverer());
@@ -26,19 +31,44 @@ namespace Riddle {
             lan_discoverer.advertise(self_server);
         }
 
+        public Client.with_dbus(DBusService service) throws Error {
+            discoverers.add (new KnownHostDiscoverer());
+            discoverers.add (new LanDiscoverer());
+            
+            // Add server associated with dbus server as local peer
+            new_server_found(new InetSocketAddress(new InetAddress.from_string(service.local_server_address()), service.local_server_port()), true);
+            
+            // Add any servers our server knows about
+            var peers = service.discovered_servers();
+            foreach (var peer in peers) {
+                var parts = peer.split(" ");
+                var address = new InetSocketAddress(new InetAddress.from_string(parts[0]), (uint16)int.parse(parts[1]));
+                new_server_found(address, service.is_peer_local(parts[0]));
+            }
+            setup();
+        }
+
         private void setup() {
             foreach (var discoverer in discoverers) {
-                discoverer.peer_discovered.connect(new_server_found);
+                discoverer.peer_discovered.connect(s => new_server_found(s, discoverer is LanDiscoverer));
                 discoverer.begin();
             }
         }
 
-        private void new_server_found(InetSocketAddress address) {
+        private void new_server_found(InetSocketAddress address, bool is_lan) {
             if(self_server != null && server_equals(address, self_server)) {
                 // Ignore self_server address
                 return;
             }
 
+            if(is_lan) {
+                if(local_servers.any(s => server_equals(s, address))) {
+                    // Ignore already discovered server
+                    return;
+                }
+                local_servers.add(address);
+            }
+
             if(servers.any(s => server_equals(s, address))) {
                 // Ignore already discovered server
                 return;
@@ -55,6 +85,15 @@ namespace Riddle {
             }
         }
 
+        public bool is_peer_local(InetAddress address) {
+            var string_address = address.to_string();
+            print(@"Is peer local? $string_address\n");
+            if(self_server != null && self_server.address.to_string() == string_address) {
+                return true;
+            }
+            return local_servers.any(s => s.address.to_string() == string_address);
+        }
+
         public int join(string group, uint port) {
             lock(join_messages) {
                 var msg = new Message(MessageType.JOIN, new string[] { group, port.to_string() }, new string[0]);
@@ -110,11 +149,14 @@ namespace Riddle {
         }
 
         public InetSocketAddress? callback(SolutionEnvelope solution, InetSocketAddress server, uint8[] author_signing_key, uint8[] reply_public_key, uint8[] reply_secret_key) throws Error {
+            print(@"[Client] Connecting to peer $(server)\n");
             var socket = new SocketClient().connect(server);
             var dis = new DataInputStream(socket.input_stream);
             var dos = new DataOutputStream(socket.output_stream);
 
+            print("[Client] Sending message\n");
             solution.to_message().send(dos);
+            print("[Client] Waiting for response\n");
             var reply = Message.from_stream(dis);
 
             if(reply.message_type == MessageType.NOT_ACCEPTED) {
@@ -122,11 +164,15 @@ namespace Riddle {
                 return null;
             }
 
+            print("[Client] Verifying response\n");
+
             var verified_reply = Solution.verify_solved_response(reply, author_signing_key);
             if(verified_reply == null) {
                 return null;
             }
 
+            print("[Client] Decrypting response\n");
+
             return Solution.decrypt_connection_details(verified_reply, reply_public_key, reply_secret_key);
 
         }

+ 21 - 0
src/lib/DBusService.vala

@@ -0,0 +1,21 @@
+
+namespace Riddle {
+
+    [DBus(name = "nz.astrolgoue.RiddleService")]
+    public interface DBusService : Object {
+
+        public abstract void join_group(string name) throws Error;
+        public abstract void leave_group(string name) throws Error;
+        
+        public abstract string local_server_address() throws Error;
+        public abstract uint16 local_server_port() throws Error;
+
+        public abstract string[] discovered_servers() throws Error;
+
+        public abstract bool is_peer_local(string address) throws Error;
+
+        public signal void received_riddle(string[] riddle_message_lines, string origin_address);
+
+    }
+
+}

+ 115 - 0
src/lib/DaemonService.vala

@@ -0,0 +1,115 @@
+
+namespace Riddle {
+
+    public sealed class DaemonService : Service, Object {
+
+        private CallbackServer callback_server;
+        private DBusService dbus_service;
+        private Client client;
+        private Invercargill.Sequence<Riddle> riddles;
+        private InetSocketAddress daemon_server_address;
+
+        public DaemonService() throws Error {
+            callback_server = new CallbackServer();
+            dbus_service = Bus.get_proxy_sync (BusType.SESSION, "nz.astrologue.RiddleService", "/nz/astrologue/RiddleService");
+            client = new Client.with_dbus (dbus_service);
+            riddles = new Invercargill.Sequence<Riddle> ();
+            
+            var daemon_inet_address = new InetAddress.from_string (dbus_service.local_server_address());
+            daemon_server_address = new InetSocketAddress (daemon_inet_address, dbus_service.local_server_port());
+            
+            dbus_service.received_riddle.connect (handle_riddle);
+            callback_server.start.begin();
+        }
+
+        public void join_group (string group) throws Error {
+            dbus_service.join_group (group);
+        }
+        public void leave_group (string group) throws Error {
+            dbus_service.leave_group (group);
+        }
+        public void publish_riddle (Riddle riddle) throws Error {
+            var envelope = riddle.seal (callback_server.socket_address.port);
+            callback_server.add_riddle (riddle, envelope);
+            client.riddle (envelope, Invercargill.single<InetSocketAddress>(daemon_server_address));
+            // Todo, throw when above call returns 0
+        }
+        public void register_riddle (Riddle riddle) {
+            lock(riddles) {
+                riddles.add (riddle);
+            }
+        }
+        private void handle_riddle (string[] riddle_message_lines, string origin_address) {
+            print("[DaemonService] Received riddle!\n");
+            try {
+                // Read envelope
+                var message = new Message.from_lines (riddle_message_lines);
+                var envelope = new RiddleEnvelope.from_message (message, new InetAddress.from_string(origin_address));
+
+                // Compile reply socket address
+                var reply_address = new InetSocketAddress (envelope.reply_address, (uint16)envelope.reply_port);
+
+                // Generate new reply keypair
+                var reply_secret_key = new uint8[Sodium.Asymmetric.Sealing.SECRET_KEY_BYTES];
+                var reply_public_key = new uint8[Sodium.Asymmetric.Sealing.PUBLIC_KEY_BYTES];
+                Sodium.Asymmetric.Sealing.generate_keypair(reply_public_key, reply_secret_key);
+
+                print("[DaemonService] Trying to solve\n");
+
+                // Solve the riddle
+                var riddle_solution = riddles
+                    .where(r => r.group == envelope.group_name)
+                    .contextualised_select<SolutionEnvelope?>(r => r.attempt (envelope, reply_public_key))
+                    .first_or_default(rs => rs.result != null);
+
+                // Check if we solved the riddle
+                if (riddle_solution?.result == null) {
+                    print("[DaemonService] We couldn't solve it\n");
+                    return;
+                }
+
+                print("[DaemonService] Seems we solved it\n");
+
+                // Callback the server
+                new Thread<bool>("Callback Thread", () => attempt_callback (riddle_solution.origin, riddle_solution.result, reply_address, envelope.author_signing_key, reply_public_key, reply_secret_key));
+
+            }
+            catch(Error e) {
+                warning("Exception handling riddle from daemon: " + e.message);
+            }
+
+        }
+        private bool attempt_callback(Riddle riddle, SolutionEnvelope solution, InetSocketAddress server, uint8[] author_signing_key, uint8[] reply_public_key, uint8[] reply_secret_key) {
+            try {
+                var peer_socket = client.callback (solution, server, author_signing_key, reply_public_key, reply_secret_key);
+
+                if(peer_socket == null) {
+                    warning("Riddle was solved, but remote peer rejected our solution.");
+                    return false;
+                }
+
+                print("[DaemonService] We discovered a peer!\n");
+
+                discovered_peer(peer_socket, riddle);
+
+                return true;
+            }
+            catch(Error e) {
+                return false;
+            }
+        }
+
+        public void publish_name (NameInfo name) throws Error {
+            client.propogate (name);
+        }
+        public Invercargill.Enumerable<NameInfo> who_is (string name) {
+            return client.who_is (name);
+        }
+
+        public bool is_peer_local (GLib.InetAddress address) throws Error {
+            return client.is_peer_local (address) || dbus_service.is_peer_local (address.to_string ());
+        }
+
+    }
+
+}

+ 1 - 1
src/lib/Riddle.vala

@@ -78,7 +78,7 @@ namespace Riddle {
         }
 
         protected abstract uint8[] generate(uint8[] secret);
-        protected abstract InetSocketAddress? validate(uint8[] solution);
+        protected abstract InetSocketAddress? validate(uint8[] data);
         protected abstract Solution? solve(uint8[] riddle);
 
         protected uint8[] join_data(uint8[] payload, uint8[] to_add) {

+ 22 - 7
src/lib/Server.vala

@@ -2,24 +2,31 @@ using Invercargill;
 
 namespace Riddle {
 
-    public class Server {
+    public delegate Message SolutionChecker(SolutionEnvelope solution);
+
+    public class Server : Object {
 
         public const int REGISTRATION_TIMEOUT_US = 600000000;
         public const int GOSSIP_INTERVAL_US = 2000000;
         public const int SYNC_INTERVAL_US = 2000000;
         public const string RIDDLE_SERVER_GROUP = "Riddle";
 
+        
         private SocketService service;
         private Gee.HashMap<string, Invercargill.Sequence<Registration>> registrations = new Gee.HashMap<string, Invercargill.Sequence<Registration>>();
         private Gee.HashMap<string, RiddleEnvelope> riddles = new Gee.HashMap<string, RiddleEnvelope>();
         private NameInfoStore names;
-        private Client client;
-
+        public Client client { get; private set; }
+        
         private Fifo<Gossip> gossip = new Fifo<Gossip>();
         private Fifo<InetSocketAddress> sync_queue = new Fifo<InetSocketAddress>();
         private Gee.HashSet<string> gossip_idempotency_tokens = new Gee.HashSet<string>();
         private uint16 port;
         
+        public SolutionChecker? solution_checker { get; set; }
+        private static SolutionChecker fallback_checker = (m) => new Message(MessageType.ERROR, new string[] { "not-implemented" }, new string[0]);
+
+        public signal void riddle_received(RiddleEnvelope riddle);
         
         public Server(string address, uint16 port, NameInfoStore name_store) throws Error {
             name_store.clean();
@@ -216,7 +223,9 @@ namespace Riddle {
                 gossip_to(riddle_envelope.group_name, to_forward.to_message(), riddle_envelope.identifier);
             }
 
+            
             // Pass to application to solve then compact when done.
+            riddle_received(riddle_envelope);
             riddle_envelope.compact();
             return new Message(MessageType.OK, new string[0], new string[0]);
         }
@@ -233,12 +242,18 @@ namespace Riddle {
             }
 
             if(riddle_metadata.reply_address == null) {
-                // TODO Verify with Riddle Object
-                return new Message(MessageType.ERROR, new string[] { "not-implemented" }, new string[0]);
+                var checker = solution_checker ?? fallback_checker;
+                return checker(solution_envelope);
+            }
+
+            var forward_socket_address = new InetSocketAddress(riddle_metadata.reply_address, (uint16)riddle_metadata.reply_port);
+            var response = client.raw_request(msg, single<InetSocketAddress>(forward_socket_address)).first_or_default();
+
+            if(response == null) {
+                return new Message(MessageType.ERROR, new string[] { "quizzer-unreachable" }, new string[0]);
             }
 
-            // TODO Forward to reply address
-            return new Message(MessageType.ERROR, new string[] { "not-implemented" }, new string[0]);
+            return response;
         }
 
         private Message handle_sync(Message msg) throws Error {

+ 35 - 0
src/lib/Service.vala

@@ -0,0 +1,35 @@
+using Invercargill;
+
+namespace Riddle {
+
+    public interface Service : Object {
+        
+        public abstract void join_group(string group) throws Error;
+        
+        public abstract void leave_group(string group) throws Error;
+        
+        public abstract void publish_riddle(Riddle riddle) throws Error;
+
+        public abstract void register_riddle(Riddle riddle);
+        
+        public abstract void publish_name(NameInfo name) throws Error;
+
+        public abstract Enumerable<NameInfo> who_is(string name);
+
+        public abstract bool is_peer_local(InetAddress address) throws Error;
+
+        public signal void discovered_peer(InetSocketAddress address, Riddle riddle);
+
+    }
+
+    public static Service construct_best_available_service() {
+        try {
+            return new DaemonService();
+        }
+        catch(Error e) {
+            warning("Could not instansiate daemon client, falling back to standalone service. Error: " + e.message);
+            return new StandaloneService();
+        }
+    }
+
+}

+ 1 - 1
src/lib/Solution.vala

@@ -7,7 +7,7 @@ namespace Riddle {
         private uint8[] secret { get; private set; }
         internal uint8[] reply_key { get; set; }
 
-        public Solution(uint8[] secret, uint8[] data) {
+        public Solution(uint8[] secret, uint8[] data) requires (data.length > 0) {
             this.data = data;
             this.secret = secret;
         }

+ 1 - 1
src/lib/SolutionEnvelope.vala

@@ -22,7 +22,7 @@ namespace Riddle {
         }
 
         public Message to_message() {
-            return new Message(MessageType.RIDDLE, 
+            return new Message(MessageType.CALLBACK, 
                 new string[] {
                     identifier
                 },

+ 29 - 0
src/lib/StandaloneService.vala

@@ -0,0 +1,29 @@
+namespace Riddle {
+
+    public sealed class StandaloneService : Service, Object {
+        
+        public void join_group (string group) {
+            assert_not_reached ();
+        }
+        public void leave_group (string group) {
+            assert_not_reached ();
+        }
+        public void publish_riddle (Riddle riddle) {
+            assert_not_reached ();
+        }
+        public void register_riddle (Riddle riddle) {
+            assert_not_reached ();
+        }
+        public void publish_name (NameInfo name) {
+            assert_not_reached ();
+        }
+        public Invercargill.Enumerable<NameInfo> who_is (string name) {
+            assert_not_reached ();
+        }
+        public bool is_peer_local (GLib.InetAddress address) {
+                assert_not_reached ();
+        }
+
+    }
+
+}

+ 5 - 0
src/lib/meson.build

@@ -33,6 +33,11 @@ sources += files('LanDiscoverer.vala')
 sources += files('RequestManager.vala')
 sources += files('NameInfoStore.vala')
 sources += files('FilesystemNameInfoStore.vala')
+sources += files('Service.vala')
+sources += files('DaemonService.vala')
+sources += files('StandaloneService.vala')
+sources += files('CallbackServer.vala')
+sources += files('DBusService.vala')
 
 
 

+ 1 - 0
src/meson.build

@@ -7,3 +7,4 @@ add_project_arguments(['--disable-warnings', '--enable-checking','--vapidir', va
 subdir('lib')
 subdir('infra')
 subdir('tests')
+subdir('sample')

+ 81 - 0
src/sample/MyAppClient.vala

@@ -0,0 +1,81 @@
+
+namespace MyApp {
+
+    private static Riddle.Service riddle_service;
+
+    public static int main(string[] args) {
+
+        try {
+
+            // Get a new riddle service
+            riddle_service = Riddle.construct_best_available_service();
+
+            // Join the app group
+            riddle_service.join_group("MyAppGroup");
+
+            // Create a client
+            var app_client = new Client(riddle_service);
+
+            // Find out who to search for
+            print("Who do you want to look up?: ");
+            var search = stdin.read_line();
+            print(@"\nFinding information for $(search)...\n");
+
+            // Do the searching!
+            app_client.lookup(search);
+
+            // Do a main loop
+            var loop = new MainLoop();
+            loop.run();
+        }
+        catch(Error e) {
+            critical("Error: " + e.message);
+            return e.code;
+        }
+
+        return 0;
+    }
+
+    class Client {
+
+        private SocketService service;
+        private uint16 port;
+
+        private Riddle.Service riddle_service;
+
+        public Client(Riddle.Service riddle_service) {
+
+            this.riddle_service = riddle_service;
+
+            service = new SocketService ();
+            port = service.add_any_inet_port(null);
+
+            service.incoming.connect(print_bio);
+            service.start();
+        }
+
+        private bool print_bio(SocketConnection connection) {
+            var peer_location = riddle_service.is_peer_local(((InetSocketAddress)connection.get_remote_address()).address) ? "local" : "internet";
+            print(@"Looks like $peer_location peer $(connection.get_remote_address().to_string()) might have an answer...\n");
+            var dis = new DataInputStream(connection.input_stream);
+            var bio = "";
+            while(!bio.has_suffix("\n\n")) {
+                bio += dis.read_line() + "\n";
+            }
+            print("Received some information:\n");
+            print(bio);
+
+            return false;
+        }
+
+        public void lookup(string name) throws Error {
+            // Create a riddle with this name, and our contact details
+            var riddle = new SpecialRiddle(name, new InetSocketAddress(new InetAddress.loopback(SocketFamily.IPV6), port));
+
+            // Publish the riddle, and hopefully someone will come back!
+            riddle_service.publish_riddle(riddle);
+        }
+        
+    }
+
+}

+ 75 - 0
src/sample/MyAppServer.vala

@@ -0,0 +1,75 @@
+
+namespace MyApp {
+
+    private static string name;
+    private static int age;
+    private static string interests;
+    private static string languages;
+
+    public static int main(string[] args) {
+        // Gather some user profile information
+        print("Welcome to MyApp, please create your profile!");
+        
+        print("\nYour name: ");
+        name = stdin.read_line();
+
+        print("\nYour age: ");
+        age = int.parse(stdin.read_line());
+
+        print("\nWhat are your interests?: ");
+        interests = stdin.read_line();
+
+        print("\nWhat languages do you speak?: ");
+        languages = stdin.read_line();
+
+        try {
+            // Get a new riddle service
+            var riddle_service = Riddle.construct_best_available_service();
+
+            // Construct our special name riddle
+            var riddle = new SpecialRiddle(name);
+
+            // Setup signal handler to send bio to new peers
+            riddle_service.discovered_peer.connect(send_bio);
+
+            // Register the riddle with the riddle service
+            riddle_service.register_riddle(riddle);
+
+            // Join the app group
+            riddle_service.join_group("MyAppGroup");
+
+            // Do a main loop
+            var loop = new MainLoop();
+            loop.run();
+        }
+        catch(Error e) {
+            critical("Error: " + e.message);
+            return e.code;
+        }
+
+        return 0;
+    }
+
+    private static void send_bio(InetSocketAddress address) {
+        print(@"Sending bio to $(address)... ");
+        var socket_client = new SocketClient();
+        var connection = socket_client.connect(address);
+
+        var bio = @"Profile information for $(name)\n";
+        bio += @"Age: $(age)\n";
+        bio += @"Is interested in: $(interests)\n";
+        bio += @"Can speak: $(languages)\n\n";
+
+        try { 
+            connection.output_stream.write(bio.data);
+            connection.close();
+            print("Sent!\n");
+        }
+        catch(Error e) {
+            print("Failed.\n");
+            warning(@"Error servicing request: $(e.message).");
+        }
+    }
+
+
+}

+ 50 - 0
src/sample/SpecialRiddle.vala

@@ -0,0 +1,50 @@
+
+namespace MyApp {
+
+    class SpecialRiddle : Riddle.Riddle {
+
+        private uint8[] name;
+        private InetSocketAddress? address;
+
+        public SpecialRiddle(string name, InetSocketAddress? server = null) {
+            this.group = "MyAppGroup";
+            this.name = name.data;
+            address = server;
+        }
+
+        protected override uint8[] generate (uint8[] secret) {
+            // Create a new array to store the riddle
+            var result = new uint8[secret.length];
+
+            // XOR the name and secret together - looping the name if it is too short
+            // NB: This is a toy example, don't roll your own crypto like this
+            for(var i = 0; i < secret.length; i++) {
+                result[i] = name[i % name.length] ^ secret[i];
+            }
+
+            // Return the riddle
+            return result;
+        }
+        protected override Riddle.Solution? solve (uint8[] riddle) {
+            // Create a new array to store the riddle secret
+            var result = new uint8[riddle.length];
+
+            // If we know the name, we can XOR it with the riddle to get the secret
+            for(var i = 0; i < riddle.length; i++) {
+                result[i] = name[i % name.length] ^ riddle[i];
+            }
+
+            // Return the solution, and the desired name as response data
+            return new Riddle.Solution (result, name);
+        }
+        protected override GLib.InetSocketAddress? validate (uint8[] data) {
+            // Validate that the expected name was sent back
+            if(new Bytes(data).compare (new Bytes(name)) == 0) {
+                return address;
+            }
+            return null;
+        }
+
+    }
+
+}

+ 11 - 0
src/sample/meson.build

@@ -0,0 +1,11 @@
+
+sources = files('MyAppServer.vala')
+sources += files('SpecialRiddle.vala')
+
+executable('sample-server', sources, dependencies: dependencies, install: false)
+
+
+sources = files('MyAppClient.vala')
+sources += files('SpecialRiddle.vala')
+
+executable('sample-client', sources, dependencies: dependencies, install: false)

+ 4 - 7
src/tests/CertifiedNameInfoTests.vala

@@ -51,15 +51,12 @@ void certified_name_info_tests() {
         var certdatum = as_datum(certtext.data);
         cert.import(ref certdatum, GnuTLS.X509.CertificateFormat.PEM);
 
-        //  cert.set_key(key);
-        //  cert.set_expiration_time((time_t)new DateTime.now_utc().add_days(5).to_unix());
+        //  var info = new CertifiedNameInfo("barrow.nz", key, (owned)cert, new GnuTLS.X509.Certificate[0], Invercargill.empty<NameProperty>());
 
-        var info = new CertifiedNameInfo("barrow.nz", key, (owned)cert, new GnuTLS.X509.Certificate[0], Invercargill.empty<NameProperty>());
+        //  var encoded = info.get_encoded();
+        //  print(@"Encoded certified name: $encoded\n");
 
-        var encoded = info.get_encoded();
-        print(@"Encoded certified name: $encoded\n");
-
-        var info2 = new CertifiedNameInfo.from_string (encoded);
+        //  var info2 = new CertifiedNameInfo.from_string (encoded);
 
     });
 }