/** * @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); }
/** * 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); } }
/** * 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; } }
/** * If sessionRader for remote node is already created - returns it, otherwise creates new {@link SessionReader} * @param remoteId id of remote node, {@link NodeInfo} will be got from {@link UDPAdapter#netConfig} * @return sessionReader */ private SessionReader getOrCreateSessionReaderCandidate(int remoteId) { NodeInfo destination = netConfig.getInfo(remoteId); if (destination == null) { callErrorCallbacks("(getOrCreateSessionReaderCandidate) unknown nodeId has received: "+remoteId); return null; } SessionReader sr = sessionReaderCandidates.computeIfAbsent(destination.getNumber(), (k)-> { SessionReader sessionReader = new SessionReader(); sessionReader.remoteNodeInfo = destination; report(logLabel, ()->"sessionReader created for nodeId "+destination.getNumber(), VerboseLevel.BASE); return sessionReader; }); return sr; }
/** * 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); } }
/** * 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); } }
/** * 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); } }