/** * Restart {@link UDPAdapter} * @throws IOException if something went wrong */ public void restartUDPAdapter() throws IOException { if(adapter != null) adapter.shutdown(); adapter = new UDPAdapter(myKey, new SymmetricKey(), myInfo, netConfig); adapter.receive(this::onReceived); adapter.addErrorsCallback(this::exceptionCallback); }
/** * When handshake procedure completes, we should send all accumulated messages. */ public void sendAllFromOutputQueue() { try { if (state.get() != Session.STATE_HANDSHAKE) { OutputQueueItem queuedItem; int maxOutputs = MAX_RETRANSMIT_QUEUE_SIZE - retransmitMap.size(); int i = 0; while ((queuedItem = outputQueue.poll()) != null) { send(queuedItem.destination, queuedItem.payload); if (i++ > maxOutputs) break; } } } catch (InterruptedException e) { callErrorCallbacks("(sendAllFromOutputQueue) InterruptedException in node " + myNodeInfo.getNumber() + ": " + e); } }
public void setUDPVerboseLevel(int level) { if(adapter != null) adapter.setVerboseLevel(level); }
/** * @see UDPAdapter#getOrCreateSession * @param remoteId id of remote node, {@link NodeInfo} will be got from {@link UDPAdapter#netConfig} * @return session */ private Session getOrCreateSession(int remoteId) { NodeInfo destination = netConfig.getInfo(remoteId); if (destination == null) { callErrorCallbacks("(getOrCreateSession) unknown nodeId has received: "+remoteId); return null; } return getOrCreateSession(destination); }
public NetworkV2(NetConfig netConfig, NodeInfo myInfo, PrivateKey myKey) throws IOException { super(netConfig); this.myInfo = myInfo; this.myKey = myKey; adapter = new UDPAdapter(myKey, new SymmetricKey(), myInfo, netConfig); // adapter.setVerboseLevel(DatagramAdapter.VerboseLevel.BASE); adapter.receive(this::onReceived); adapter.addErrorsCallback(this::exceptionCallback); }
DatagramAdapter d1 = new UDPAdapter(TestKeys.privateKey(0), new SymmetricKey(), node1, nc); // create implemented class with node1 DatagramAdapter d2 = new UDPAdapter(TestKeys.privateKey(1), new SymmetricKey(), node2, nc); // create implemented class with node1 DatagramAdapter d3 = new UDPAdapter(TestKeys.privateKey(2), new SymmetricKey(), node3, nc); // create implemented class with node1
/** * When someone send us {@link PacketTypes#HELLO} typed {@link UDPAdapter.Packet}, * we should respond with {@link PacketTypes#WELCOME}. * @param sessionReader is {@link UDPAdapter.SessionReader} in which sending is. */ private void sendWelcome(SessionReader sessionReader) { try { report(logLabel, () -> "send welcome to " + sessionReader.remoteNodeInfo.getNumber(), VerboseLevel.BASE); byte[] data = sessionReader.localNonce; byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(data, HashType.SHA512); byte[] payload = Boss.dumpToArray(Arrays.asList(data, sign)); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.WELCOME, payload); sendPacket(sessionReader.remoteNodeInfo, packet); sessionReader.removeHandshakePacketsFromRetransmitMap(); sessionReader.addPacketToRetransmitMap(packet.packetId, packet, sessionReader.localNonce); } catch (EncryptionError e) { callErrorCallbacks("(sendWelcome) EncryptionError: " + e); } }
/** * We have sent {@link PacketTypes#HELLO} typed {@link Packet}, * and have got {@link PacketTypes#WELCOME} typed {@link Packet} - it means we can continue handshake and send * request for session's keys. KEY_REQ's payload is more than 512 bytes, so used two parts here. * @param session is {@link Session} in which sending is. * @param payloadPart1 is prepared in {@link SocketListenThread#onReceiveWelcome(Packet)} * @param payloadPart2 is prepared in {@link SocketListenThread#onReceiveWelcome(Packet)} */ private void sendKeyReq(Session session, byte[] payloadPart1, byte[] payloadPart2) throws EncryptionError { report(logLabel, ()->"send key_req to "+session.remoteNodeInfo.getNumber(), VerboseLevel.BASE); Packet packet1 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.KEY_REQ_PART1, payloadPart1); Packet packet2 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.KEY_REQ_PART2, payloadPart2); sendPacket(session.remoteNodeInfo, packet1); sendPacket(session.remoteNodeInfo, packet2); session.addPacketToRetransmitMap(packet1.packetId, packet1, payloadPart1); session.addPacketToRetransmitMap(packet2.packetId, packet2, payloadPart2); }
DatagramAdapter d1 = new UDPAdapter(TestKeys.privateKey(0), new SymmetricKey(), node1, nc); DatagramAdapter d2 = new UDPAdapter(TestKeys.privateKey(1), new SymmetricKey(), node2, nc); ((UDPAdapter)d1).printInternalState(); ((UDPAdapter)d2).printInternalState(); Thread.sleep(UDPAdapter.RETRANSMIT_MAX_ATTEMPTS*UDPAdapter.RETRANSMIT_TIME+1000); d1.shutdown(); d2.shutdown(); ((UDPAdapter)d1).printInternalState(); ((UDPAdapter)d2).printInternalState();
@Override synchronized public void send(NodeInfo destination, byte[] payload) throws InterruptedException { report(logLabel, () -> "send to "+destination.getNumber()+", isActive: "+socketListenThread.isActive.get(), VerboseLevel.DETAILED); if (!socketListenThread.isActive.get()) return; Session session = getOrCreateSession(destination); if (session.state.get() == Session.STATE_HANDSHAKE) { session.addPayloadToOutputQueue(destination, payload); } else { if (session.retransmitMap.size() > MAX_RETRANSMIT_QUEUE_SIZE) session.addPayloadToOutputQueue(destination, payload); else sendPayload(session, payload); } }
/** * Creates {@link Packet} of type {@link PacketTypes#DATA} and sends it to network, initiates retransmission. * It is normal data sending procedure when {@link Session} with remote node is already established. * @param session {@link Session} with remote node * @param payload data to send */ private void sendPayload(Session session, byte[] payload) { byte[] dataToSend = preparePayloadForSession(session.sessionKey, payload); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.DATA, dataToSend); sendPacket(session.remoteNodeInfo, packet); session.addPacketToRetransmitMap(packet.packetId, packet, payload); }
/** * Each adapter will try to send blocks until have got special {@link Packet} with type {@link PacketTypes#ACK}, * that means receiver have got block. So when we got block, but something went wrong - call this method. Note that * for success blocks needs to call {@link UDPAdapter#sendAck(SessionReader, Integer)} * @param nodeNumber node id in which sending is * @param packetId is id of block we have got. */ private void sendNack(Integer nodeNumber, Integer packetId) { try { NodeInfo destination = netConfig.getInfo(nodeNumber); if (destination != null) { report(logLabel, ()->"send nack to "+nodeNumber, VerboseLevel.DETAILED); byte[] randomSeed = Do.randomBytes(64); byte[] data = Boss.dumpToArray(Arrays.asList(packetId, randomSeed)); byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(data, HashType.SHA512); byte[] payload = Boss.dumpToArray(Arrays.asList(data, sign)); Packet packet = new Packet(0, myNodeInfo.getNumber(), nodeNumber, PacketTypes.NACK, payload); sendPacket(destination, packet); } } catch (EncryptionError e) { callErrorCallbacks("(sendNack) can't send NACK, EncryptionError: " + e); } }
public void shutdown() { if(adapter != null) adapter.shutdown(); }
/** * Method creates {@link DatagramPacket} from given {@link Packet} and sends it to address:port from destination. * @param destination instance of {@link NodeInfo} with net address for sending. * @param packet data to send. It's {@link Packet#makeByteArray()} should returns data with size less than {@link DatagramAdapter#MAX_PACKET_SIZE} */ private void sendPacket(NodeInfo destination, Packet packet) { byte[] payload = packet.makeByteArray(); DatagramPacket dp = new DatagramPacket(payload, payload.length, destination.getNodeAddress().getAddress(), destination.getNodeAddress().getPort()); try { report(logLabel, ()->"sendPacket datagram size: " + payload.length, VerboseLevel.DETAILED); if ((testMode == TestModes.LOST_PACKETS || testMode == TestModes.LOST_AND_SHUFFLE_PACKETS) && (new Random().nextInt(100) < lostPacketsPercent)) report(logLabel, ()->"test mode: skip socket.send", VerboseLevel.BASE); else socket.send(dp); } catch (Exception e) { callErrorCallbacks("sendPacket exception: " + e); } }
protected void sendHello(NodeInfo myNodeInfo, NodeInfo destination, UDPAdapter udpAdapter, DatagramSocket socket) throws InterruptedException { // System.out.println(">> send froud from " + myNodeInfo.getNumber() + " to " + destination.getNumber()); Binder binder = Binder.fromKeysValues( "data", myNodeInfo.getNumber() ); UDPAdapter.Packet packet = udpAdapter.createTestPacket( new Random().nextInt(Integer.MAX_VALUE), myNodeInfo.getNumber(), destination.getNumber(), UDPAdapter.PacketTypes.HELLO, Boss.pack(binder)); sendBlock(packet, socket, destination); }
/** * All packets data ({@link Packet#payload}) of type {@link PacketTypes#DATA} * must be encrypted with sessionKey ({@link SymmetricKey}). * This method implements encryption procedure for it. * @param sessionKey key for encryption * @param payload data to encrypt * @return encrypted data, ready for sending to network */ private byte[] preparePayloadForSession(SymmetricKey sessionKey, byte[] payload) { try { byte[] payloadWithRandomChunk = new byte[payload.length + 2]; System.arraycopy(payload, 0, payloadWithRandomChunk, 0, payload.length); System.arraycopy(Bytes.random(2).toArray(), 0, payloadWithRandomChunk, payload.length, 2); byte[] encryptedPayload = new SymmetricKey(sessionKey.getKey()).etaEncrypt(payloadWithRandomChunk); byte[] crc32 = new Crc32().digest(encryptedPayload); byte[] dataToSend = new byte[encryptedPayload.length + crc32.length]; System.arraycopy(encryptedPayload, 0, dataToSend, 0, encryptedPayload.length); System.arraycopy(crc32, 0, dataToSend, encryptedPayload.length, crc32.length); return dataToSend; } catch (EncryptionError e) { callErrorCallbacks("(preparePayloadForSession) EncryptionError: " + e); return payload; } }
public TestLocalNetwork(NetConfig netConfig, NodeInfo myInfo, PrivateKey myKey) throws IOException { super(netConfig); this.myInfo = myInfo; this.myKey = myKey; adapter = new UDPAdapter(myKey, new SymmetricKey(), myInfo, netConfig); // adapter.setVerboseLevel(DatagramAdapter.VerboseLevel.BASE); adapter.receive(this::onReceived); adapter.addErrorsCallback(this::exceptionCallback); }
DatagramAdapter d1 = new UDPAdapter(TestKeys.privateKey(0), symmetricKey1, node1, nc); // create implemented class with node1 DatagramAdapter d = new UDPAdapter(TestKeys.privateKey(keyIndex), sk, n, nc); // create implemented class with node1
/** * This is first step of creation and installation of the session. * @param session is {@link Session} in which sending is. */ private void sendHello(Session session) { try { report(logLabel, () -> "send hello to " + session.remoteNodeInfo.getNumber(), VerboseLevel.BASE); byte[] helloNonce = Do.randomBytes(64); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.HELLO, new PublicKey(session.remoteNodeInfo.getPublicKey().pack()).encrypt(helloNonce)); sendPacket(session.remoteNodeInfo, packet); session.addPacketToRetransmitMap(packet.packetId, packet, helloNonce); } catch (EncryptionError e) { callErrorCallbacks("(sendHello) EncryptionError: " + e); } }
/** * Someone who sent {@link PacketTypes#HELLO} typed {@link Packet}, * send us new KEY_REQ typed {@link Packet} - if all is ok we send session keys to. * SESSION's payload is more than 512 bytes, so used two parts here. * From now we ready to data exchange. * @param sessionReader is {@link SessionReader} in which sending is. */ private void sendSessionKey(SessionReader sessionReader) throws EncryptionError { report(logLabel, ()->"send session_key to "+sessionReader.remoteNodeInfo.getNumber(), VerboseLevel.BASE); List data = Arrays.asList(sessionReader.sessionKey.getKey(), sessionReader.remoteNonce); byte[] packed = Boss.pack(data); byte[] encrypted = new PublicKey(sessionReader.remoteNodeInfo.getPublicKey().pack()).encrypt(packed); byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(encrypted, HashType.SHA512); Packet packet1 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.SESSION_PART1, encrypted); Packet packet2 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.SESSION_PART2, sign); sendPacket(sessionReader.remoteNodeInfo, packet1); sendPacket(sessionReader.remoteNodeInfo, packet2); sessionReader.addPacketToRetransmitMap(packet1.packetId, packet1, encrypted); sessionReader.addPacketToRetransmitMap(packet2.packetId, packet2, sign); }