瀏覽代碼

Add IPv4 Support

Billy Barrow 3 年之前
父節點
當前提交
7e4a83e980

+ 182 - 0
src/lib/Networks/IPv4/IPv4.vala

@@ -0,0 +1,182 @@
+using LibPeer.Networks;
+using LibPeer.Protocols.Mx2;
+using LibPeer.Util;
+
+using Gee;
+
+namespace LibPeer.Networks.IPv4 {
+
+    public class IPv4 : Network {
+
+        private IPv4PeerInfo local_peer;
+        private InetSocketAddress socket_address;
+        private Socket socket;
+        private Socket multicast_socket;
+        private InetSocketAddress multicast_address;
+        private HashSet<InstanceReference> advertised_instances = new HashSet<InstanceReference>((a) => a.hash(), (a, b) => a.compare(b) == 0);
+
+        private static uint8[] multicast_magic_number = new uint8[] {'L', 'i', 'b', 'P', 'e', 'e', 'r', '2', '-', 'I', 'P', 'v', '4', ':'};
+        private const uint8 DGRAM_DATA = 0;
+        private const uint8 DGRAM_INQUIRE = 1;
+        private const uint8 DGRAM_INSTANCE = 2;
+
+        public IPv4(string address, uint16 port) {
+            socket = new Socket(SocketFamily.IPV4, SocketType.DATAGRAM, SocketProtocol.UDP);
+            multicast_socket = new Socket(SocketFamily.IPV4, SocketType.DATAGRAM, SocketProtocol.UDP);
+            var inet_address = new InetAddress.from_string(address);
+            socket_address = new InetSocketAddress(inet_address, port);
+            local_peer = new IPv4PeerInfo(socket_address);
+            multicast_address = new InetSocketAddress(new InetAddress.from_string("224.0.0.3"), 1199);
+        }
+
+        public override Bytes get_network_identifier() {
+            return new Bytes({'I', 'P', 'v', '4'});
+        }
+
+        public override void bring_up() throws IOError, Error {
+            // Bind the main socket
+            socket.bind(socket_address, false);
+
+            // Setup multicast socket
+            multicast_socket.bind(multicast_address, true);
+            multicast_socket.join_multicast_group(multicast_address.get_address(), false, null);
+
+            new Thread<bool>("LibPeer IPv4 Listener", listen);
+            new Thread<bool>("LibPeer IPv4 Local Discovery", multicast_listen);
+        }
+
+        public override void bring_down() throws IOError, Error {
+            warning("[IPv4] Bring down not yet implemented...\n");
+        }
+
+        public override void advertise(InstanceReference instance_reference) throws IOError, Error {
+            var stream = new MemoryOutputStream(null, GLib.realloc, GLib.free);
+            var dos = new DataOutputStream(stream);
+            dos.byte_order = DataStreamByteOrder.BIG_ENDIAN;
+
+            dos.write(multicast_magic_number);
+            dos.put_uint16(socket_address.get_port());
+            instance_reference.serialise(dos);
+
+            stream.flush();
+            stream.close();
+            uint8[] buffer = stream.steal_data();
+            buffer.length = (int)stream.get_data_size();
+
+            multicast_socket.send_to(multicast_address, buffer);
+            advertised_instances.add(instance_reference);
+        }
+
+        public override void send(uint8[] bytes, PeerInfo peer_info) throws IOError, Error {
+            var ipv4_info = (IPv4PeerInfo)peer_info;
+            var buffer = new ByteComposer().add_byte(DGRAM_DATA).add_byte_array(bytes).to_byte_array();
+            socket.send_to(ipv4_info.to_socket_address(), buffer);
+        }
+
+        private bool listen() {
+            while(true) {
+                try {
+                    // Receive the next datagram
+                    var buffer = new uint8[65536];
+                    SocketAddress address;
+                    var size = socket.receive_from(out address, buffer);
+                    buffer.length = (int)size;
+
+                    // Put the datagram into a stream
+                    var stream = new MemoryInputStream.from_data(buffer);
+
+                    // Create peer info
+                    var info = new IPv4PeerInfo((InetSocketAddress)address);
+
+                    // Read the datagram type
+                    var type = new uint8[1];
+                    stream.read(type);
+
+                    switch (type[0]) {
+                        case DGRAM_DATA:
+                            // Create a new receiption
+                            var receiption = new Receiption(stream, info, this);
+
+                            // Pass up
+                            incoming_receiption(receiption);
+                            break;
+                        
+                        case DGRAM_INQUIRE:
+                            // Respond with instance information
+                            foreach (var instance in advertised_instances) {
+                                // Send the instance information as a single datagram
+                                var payload = new ByteComposer().add_byte(DGRAM_INSTANCE).add_bytes(instance.to_bytes()).to_byte_array();
+                                socket.send_to(address, payload);
+                            }
+                            break;
+
+                        case DGRAM_INSTANCE:
+                            // Create the instance reference
+                            var instance_reference = new InstanceReference.from_stream(stream);
+
+                            // Is the instance one we advertise?
+                            if(advertised_instances.contains(instance_reference)) {
+                                // Yes, skip
+                                continue;
+                            }
+
+                            // Create the advertisement
+                            var advertisement = new Advertisement(instance_reference, info);
+
+                            // Send to the application
+                            incoming_advertisment(advertisement);
+                            break;
+                    }
+                }
+                catch(Error e) {
+                    error(@"Exception on incoming packet: $(e.message)");
+                }
+            }
+            return false;
+        }
+
+        private bool multicast_listen() {
+            while(true) {
+                // Receive the next discovery datagram
+                var buffer = new uint8[InstanceReference.SERIALISED_SIZE + 16];
+                SocketAddress address;
+                multicast_socket.receive_from(out address, buffer);
+                var inet_address = (InetSocketAddress)address;
+
+                var stream = new MemoryInputStream.from_data(buffer);
+                var dis = new DataInputStream(stream);
+
+                var magic_number = dis.read_bytes(multicast_magic_number.length);
+                if(magic_number.compare(new Bytes(multicast_magic_number)) != 0) {
+                    // Invalid magic number
+                    continue;
+                }
+
+                // Get advertised port number
+                var port = dis.read_uint16();
+
+                // Create peer info
+                var info = new IPv4PeerInfo(new InetSocketAddress(inet_address.get_address(), port));
+
+                // Create the instance reference
+                var instance_reference = new InstanceReference.from_stream(dis);
+
+                // Is the instance one we advertise?
+                if(advertised_instances.contains(instance_reference)) {
+                    // Yes, skip
+                    continue;
+                }
+
+                // Create the advertisement
+                var advertisement = new Advertisement(instance_reference, info);
+
+                // Send to the application
+                incoming_advertisment(advertisement);
+                
+            }
+            return false;
+        }
+
+    }
+
+}

+ 95 - 0
src/lib/Networks/IPv4/IPv4PeerInfo.vala

@@ -0,0 +1,95 @@
+
+using LibPeer.Networks;
+
+namespace LibPeer.Networks.IPv4 {
+
+    public class IPv4PeerInfo : PeerInfo {
+
+        internal uint8[] address = new uint8[4];
+        internal uint16 port = 0;
+
+        internal IPv4PeerInfo(InetSocketAddress socket_address) {
+            register_info_type();
+            this.address = ip_string_to_bytes(socket_address.get_address().to_string());
+            this.port = socket_address.get_port();
+        }
+
+        public override GLib.Bytes get_network_identifier () {
+            return new Bytes({'I', 'P', 'v', '4'});
+        }
+
+        protected override void build(uint8 data_length, InputStream stream, Bytes network_type) throws Error
+        requires (data_length == 6) {
+            DataInputStream dis = new DataInputStream(stream);
+            dis.byte_order = DataStreamByteOrder.BIG_ENDIAN;
+            
+            for(int i = 0; i < 4; i ++) {
+                address[i] = dis.read_byte();
+            }
+
+            port = dis.read_uint16();
+        }
+
+        protected override Bytes get_data_segment() {
+            var stream = new MemoryOutputStream(null, GLib.realloc, GLib.free);
+            var dos = new DataOutputStream(stream);
+            dos.byte_order = DataStreamByteOrder.BIG_ENDIAN;
+
+            for(int i = 0; i < 4; i ++) {
+                dos.put_byte(address[i]);
+            }
+
+            dos.put_uint16(port);
+            dos.flush();
+            dos.close();
+
+            uint8[] buffer = stream.steal_data();
+            buffer.length = (int)stream.get_data_size();
+            return new Bytes(buffer);
+        }
+
+        public override bool equals (PeerInfo other) {
+            if (other is IPv4PeerInfo) {
+                var oth = (IPv4PeerInfo)other;
+                for(int i = 0; i < 4; i++) {
+                    if (oth.address[i] != address[i]) {
+                        return false;
+                    }
+                }
+                if(oth.port != port) {
+                    return false;
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public override uint hash() {
+            // XXX I'm sure this is the opposite of efficient
+            return get_data_segment().hash();
+        }
+
+        public override string to_string() {
+            return @"IPv4://$(bytes_to_ip_string(address)):$(port)";
+        }
+
+        public InetSocketAddress to_socket_address() {
+            InetAddress inet_address = new InetAddress.from_string(bytes_to_ip_string(address));
+            return new InetSocketAddress(inet_address, port);
+        }
+
+        private static uint8[] ip_string_to_bytes(string ip) {
+            var parts = ip.split(".", 4);
+            var data = new uint8[4];
+            for(int i = 0; i < 4; i++) {
+                data[i] = (uint8)int.parse(parts[i]);
+            }
+            return data;
+        }
+
+        private static string bytes_to_ip_string(uint8[] data) {
+            return @"$(data[0]).$(data[1]).$(data[2]).$(data[3])";
+        }
+    }
+
+}

+ 1 - 1
src/lib/Protocols/AIP/ApplicationInformationProtocol.vala

@@ -311,7 +311,7 @@ namespace LibPeer.Protocols.Aip {
                 handle_request(stream);
             }
             else {
-                print("RX Stream: Invalid (stream closed)\n");
+                print(@"RX Stream: Invalid following $(following) (stream closed)\n");
                 stream.close();
             }
 

+ 10 - 10
src/lib/Protocols/MX2/InstanceReference.vala

@@ -5,6 +5,7 @@ namespace LibPeer.Protocols.Mx2 {
 
         public uint8[] verification_key { get; private set; }
         public uint8[] public_key { get; private set; }
+        public const int SERIALISED_SIZE = 64;
 
         public InstanceReference(uint8[] verification_key, uint8[] public_key) 
         requires (verification_key.length == 32)
@@ -27,20 +28,19 @@ namespace LibPeer.Protocols.Mx2 {
             stream.write(public_key);
         }
 
-        private Bytes combined_bytes () {
-            uint8[] combined = new Util.ByteComposer()
-                .add_byte_array(verification_key)
-                .add_byte_array(public_key)
-                .to_byte_array();
-            return new Bytes(combined);
-        }
-
         public uint hash() {
-            return combined_bytes().hash();
+            return to_bytes().hash();
         }
 
         public int compare(InstanceReference other) {
-            return combined_bytes().compare(other.combined_bytes());
+            return to_bytes().compare(other.to_bytes());
+        }
+
+        public Bytes to_bytes() {
+            return new Util.ByteComposer()
+            .add_byte_array(verification_key)
+            .add_byte_array(public_key)
+            .to_bytes();
         }
 
     }

+ 2 - 0
src/lib/meson.build

@@ -24,6 +24,8 @@ sources += files('Networks/Simulation/NetSimPeerInfo.vala')
 sources += files('Networks/Simulation/Conduit.vala')
 sources += files('Networks/Simulation/NetSim.vala')
 sources += files('Networks/Simulation/Packet.vala')
+sources += files('Networks/IPv4/IPv4.vala')
+sources += files('Networks/IPv4/IPv4PeerInfo.vala')
 sources += files('Protocols/MX2/Muxer.vala')
 sources += files('Protocols/MX2/Frame.vala')
 sources += files('Protocols/MX2/Inquiry.vala')

+ 2 - 2
src/toys/discoverer/Discoverer.vala

@@ -16,9 +16,9 @@ namespace Discoverer {
         private Instance app_instance;
         private int id;
 
-        public DiscoverWorker(int id, Conduit conduit) throws Error, IOError {
+        public DiscoverWorker(int id, Network net) throws Error, IOError {
             this.id = id;
-            network = conduit.get_interface (200, 400, 0.0f);
+            network = net;
             network.bring_up();
             print("Instansiate\n");
             aip = new ApplicationInformationProtocol(muxer);

+ 1 - 1
src/toys/discoverer/Main.vala

@@ -12,7 +12,7 @@ namespace Discoverer {
 
             DiscoverWorker[] pingas = new DiscoverWorker[count];
             for (int i = 0; i < count; i++){
-                pingas[i] = new DiscoverWorker(i, conduit);
+                pingas[i] = new DiscoverWorker(i, conduit.get_interface (10, 10, 0.0f));
             }
 
             while(true) {};

+ 20 - 0
src/toys/discoverer/MainIP.vala

@@ -0,0 +1,20 @@
+using LibPeer.Networks.IPv4;
+
+namespace Discoverer {
+
+    class Main : Object {
+
+        public static int main(string[] args) {
+            print("Discoverer (IPv4)\n");
+            string address = args[1];
+            uint16 port = (uint16)int.parse(args[2]);
+
+            var worker = new DiscoverWorker(0, new IPv4(address, port));
+
+            while(true) {}
+
+            return 0;
+        }
+    }
+
+}

+ 5 - 0
src/toys/discoverer/meson.build

@@ -10,3 +10,8 @@ sources = files('Main.vala')
 sources += files('Discoverer.vala')
 
 executable('discoverer', sources, dependencies: dependencies)
+
+sources = files('MainIP.vala')
+sources += files('Discoverer.vala')
+
+executable('discoverer_ipv4', sources, dependencies: dependencies)

+ 2 - 2
src/toys/give_file/GiveFile.vala

@@ -18,10 +18,10 @@ namespace GiveFile {
         private HashSet<InstanceReference> peers = new HashSet<InstanceReference>(r => r.hash(), (a, b) => a.compare(b) == 0);
         private MainLoop loop;
 
-        public FileGiver(Conduit conduit, string file_path) {
+        public FileGiver(Network net, string file_path) {
             loop = new MainLoop();
             muxer = new Muxer ();
-            network = conduit.get_interface (0, 0, 0.0f);
+            network = net;
             network.bring_up ();
             muxer.register_network (network);
             instance = muxer.create_instance ("GiveFile");

+ 1 - 1
src/toys/give_file/Main.vala

@@ -11,7 +11,7 @@ namespace GiveFile {
 
             FileGiver[] givers = new FileGiver[args.length-1];
             for(int i = 1; i < args.length; i++) {
-                givers[i-1] = new FileGiver(conduit, args[i]);
+                givers[i-1] = new FileGiver(conduit.get_interface (0, 0, 0.0f), args[i]);
             }
 
             while(true) {};

+ 20 - 0
src/toys/give_file/MainIP.vala

@@ -0,0 +1,20 @@
+using LibPeer.Networks.IPv4;
+
+namespace GiveFile {
+
+    class Main : Object {
+
+        public static int main(string[] args) {
+            print("Give File (IPv4)\n");
+            string address = args[1];
+            uint16 port = (uint16)int.parse(args[2]);
+
+            var worker = new FileGiver(new IPv4(address, port), args[3]);
+
+            while(true) {}
+
+            return 0;
+        }
+    }
+
+}

+ 5 - 0
src/toys/give_file/meson.build

@@ -10,3 +10,8 @@ sources = files('Main.vala')
 sources += files('GiveFile.vala')
 
 executable('give_file', sources, dependencies: dependencies)
+
+sources = files('MainIP.vala')
+sources += files('GiveFile.vala')
+
+executable('give_file_ipv4', sources, dependencies: dependencies)