From 5c89bc6957251ad5fbc3ccea43f8d0778cbdd87e Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 19 Mar 2020 17:31:03 +0100 Subject: [PATCH] Add protocol elements for tracker Needs to implements tracker now. --- src/protocolP2P/CodeType.java | 1 + src/protocolP2P/DiscoverRequest.java | 65 +++++++++++++ src/protocolP2P/DiscoverResponse.java | 111 ++++++++++++++++++++++ src/protocolP2P/FilePart.java | 2 +- src/protocolP2P/Payload.java | 39 +++++--- src/protocolP2P/ProtocolP2PPacket.java | 7 ++ src/protocolP2P/ProtocolP2PPacketTCP.java | 12 +++ src/protocolP2P/ProtocolP2PPacketUDP.java | 13 ++- src/protocolP2P/RequestResponseCode.java | 7 +- src/remoteException/NotATracker.java | 7 ++ src/serverP2P/ServerManagementTCP.java | 25 ++++- src/serverP2P/ServerManagementUDP.java | 17 ++++ src/tools/BytesArrayTools.java | 31 ++++++ src/tools/HostItem.java | 14 +++ 14 files changed, 334 insertions(+), 17 deletions(-) create mode 100644 src/protocolP2P/DiscoverRequest.java create mode 100644 src/protocolP2P/DiscoverResponse.java create mode 100644 src/remoteException/NotATracker.java diff --git a/src/protocolP2P/CodeType.java b/src/protocolP2P/CodeType.java index e38de49..87f3e49 100644 --- a/src/protocolP2P/CodeType.java +++ b/src/protocolP2P/CodeType.java @@ -8,6 +8,7 @@ package protocolP2P; */ public enum CodeType { REQUEST, + REQUEST_TRACKER, RESPONSE, ERROR } diff --git a/src/protocolP2P/DiscoverRequest.java b/src/protocolP2P/DiscoverRequest.java new file mode 100644 index 0000000..b3a83aa --- /dev/null +++ b/src/protocolP2P/DiscoverRequest.java @@ -0,0 +1,65 @@ +package protocolP2P; +import protocolP2P.Payload; +import tools.BytesArrayTools; +import localException.InternalError; +import localException.SizeError; +import localException.ProtocolError; +import localException.TransmissionError; + +public class DiscoverRequest extends Payload { + + private String filename; + + /** Constructor with filename (typically used by client). If filename is null, it is initialized with "". + * @param filename Name of the file you want a server list of. + * @throws InternalError + */ + public DiscoverRequest(String filename) throws InternalError { + super(RequestResponseCode.DISCOVER_REQUEST); + if (filename == null) { + this.filename = ""; + } else { + this.filename = filename; + } + } + + /** Constructor (typically used by server) with a byte[] parameter containing the Packet received. + * @param packet the full Packet received + * @throws SizeError + * @throws InternalError + * @throws ProtocolError + * @throws TransmissionError + */ + protected DiscoverRequest(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError { + super(packet); + int size = getPayloadSize(packet); + filename = BytesArrayTools.readString(packet, Payload.PAYLOAD_START_POSITION, size); + } + + /** Returns a byte[] containing Packet with padding. + * This Packet is still incomplete and should not be send directly. + * ProtocolP2PPacket will use this method to generate the complete Packet. + * @return Packet with padding + * @throws InternalError + */ + protected byte[] toPacket() throws InternalError { + // compute total size + int size = PAYLOAD_START_POSITION + filename.length(); + byte[] packet = new byte[size + 1]; // java initialize all to zero + // set request/response code + packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; + // set Payload size + setPayloadSize(size - PAYLOAD_START_POSITION, packet); + // write filename to Packet + BytesArrayTools.write(packet, filename, PAYLOAD_START_POSITION); + return packet; + } + + /** Filename getter. + * @return filename + */ + public String getFilename() { + return filename; + } + +} diff --git a/src/protocolP2P/DiscoverResponse.java b/src/protocolP2P/DiscoverResponse.java new file mode 100644 index 0000000..b887dea --- /dev/null +++ b/src/protocolP2P/DiscoverResponse.java @@ -0,0 +1,111 @@ +package protocolP2P; +import protocolP2P.Payload; +import tools.HostItem; +import java.util.ArrayList; +import java.util.List; +import localException.InternalError; +import localException.SizeError; +import localException.ProtocolError; +import localException.TransmissionError; +import tools.BytesArrayTools; + +public class DiscoverResponse extends Payload { + + private List hostList; + private String filename; + private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION; + private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4; + + /** Constructor with filename (typically used by tracker). If filename is null, it is initialized with "". + * @param filename Name of the file related to the server list. + * @param hostList List of servers + * @throws InternalError + */ + public DiscoverResponse(String filename, List hostList) throws InternalError { + super(RequestResponseCode.DISCOVER_RESPONSE); + this.filename = filename; + this.hostList = hostList; + } + + /** Constructor (typically used by server) with a byte[] parameter containing the Packet received. + * @param packet the full Packet received + * @throws SizeError + * @throws InternalError + * @throws ProtocolError + * @throws TransmissionError + */ + protected DiscoverResponse(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError { + super(packet); + int size = getPayloadSize(packet); + /* Read filename size */ + int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION); + + /* Read filename */ + filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize); + + // TODO + hostList = new ArrayList<>(); + int i = FILENAME_POSITION + filenameSize; + while(i getHostList() { + return hostList; + } + + /** Filename getter. + * @return filename + */ + public String getFilename() { + return filename; + } +} diff --git a/src/protocolP2P/FilePart.java b/src/protocolP2P/FilePart.java index 0863c0b..c394b0f 100644 --- a/src/protocolP2P/FilePart.java +++ b/src/protocolP2P/FilePart.java @@ -83,7 +83,7 @@ public class FilePart extends Payload { // set request/response code packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; // set Payload size - setPayloadSize(size - OFFSET_POSITION, packet); + setPayloadSize(size - PAYLOAD_START_POSITION, packet); // write offset to Packet BytesArrayTools.write(packet, OFFSET_POSITION, offset); // write totalSize to Packet diff --git a/src/protocolP2P/Payload.java b/src/protocolP2P/Payload.java index f0dcb34..8a84384 100644 --- a/src/protocolP2P/Payload.java +++ b/src/protocolP2P/Payload.java @@ -5,6 +5,8 @@ import protocolP2P.FileList; import protocolP2P.LoadRequest; import protocolP2P.HashRequest; import protocolP2P.HashResponse; +import protocolP2P.DiscoverRequest; +import protocolP2P.DiscoverResponse; import localException.ProtocolError; import localException.InternalError; import localException.TransmissionError; @@ -27,11 +29,13 @@ public class Payload { */ public Payload(RequestResponseCode requestResponseCode) throws InternalError { /* asserts to help debugging */ - assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; - assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; - assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; - assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; - assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; + assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; + assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; + assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; + assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; + assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; + assert requestResponseCode != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class"; + assert requestResponseCode != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class"; this.requestResponseCode = requestResponseCode; checkRequestResponseCode(); // this can throw InternalError } @@ -46,13 +50,17 @@ public class Payload { */ protected Payload(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError { /* asserts to help debugging */ - assert getPayloadSize(packet) + 8 <= packet.length : "Payload is truncated"; - if (packet.length < getPayloadSize(packet) + 8) { + assert getPayloadSize(packet) + PAYLOAD_START_POSITION <= packet.length : "Payload is truncated"; + if (packet.length < getPayloadSize(packet) + PAYLOAD_START_POSITION) { throw new TransmissionError(); } - assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; - assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; - assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class"; + assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class"; requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]); checkRequestResponseCode(); // this can throw InternalError } @@ -62,9 +70,14 @@ public class Payload { */ private void checkRequestResponseCode() throws InternalError { /* Incorrect use cases (use subclasses instead) */ - if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList)) - || (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart)) - || (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) { + if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList)) + || (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart)) + || (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest)) + || (requestResponseCode == RequestResponseCode.HASH_REQUEST && !(this instanceof HashRequest)) + || (requestResponseCode == RequestResponseCode.HASH_RESPONSE && !(this instanceof HashResponse)) + || (requestResponseCode == RequestResponseCode.DISCOVER_REQUEST && !(this instanceof DiscoverRequest)) + || (requestResponseCode == RequestResponseCode.DISCOVER_RESPONSE && !(this instanceof DiscoverResponse)) + ) { throw new InternalError(); } } diff --git a/src/protocolP2P/ProtocolP2PPacket.java b/src/protocolP2P/ProtocolP2PPacket.java index be23aec..037da9b 100644 --- a/src/protocolP2P/ProtocolP2PPacket.java +++ b/src/protocolP2P/ProtocolP2PPacket.java @@ -13,6 +13,7 @@ import remoteException.VersionRemoteError; import remoteException.EmptyFile; import java.net.InetAddress; import java.io.IOException; +import tools.HostItem; /** Representation of packet. * @author Louis Royer @@ -50,6 +51,12 @@ public abstract class ProtocolP2PPacket { */ public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed; + /** Get hostItem of the sender + * @return hostItem of the sender + * @throws InternalError + */ + public abstract HostItem getHostItem() throws InternalError; + /** Receive a response * @throws EmptyFile * @throws NotFound diff --git a/src/protocolP2P/ProtocolP2PPacketTCP.java b/src/protocolP2P/ProtocolP2PPacketTCP.java index 7c80474..ddde5f3 100644 --- a/src/protocolP2P/ProtocolP2PPacketTCP.java +++ b/src/protocolP2P/ProtocolP2PPacketTCP.java @@ -12,6 +12,7 @@ import remoteException.ProtocolRemoteError; import remoteException.VersionRemoteError; import remoteException.EmptyFile; import tools.BytesArrayTools; +import tools.HostItem; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import protocolP2P.LoadRequest; @@ -319,4 +320,15 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { return packet; } + /** Get hostItem of the sender + * @return hostItem of the sender + * @throws InternalError + */ + public HostItem getHostItem() throws InternalError { + if (responseSocket == null) { + throw new InternalError(); + } + return new HostItem(responseSocket.getInetAddress().getHostName(), responseSocket.getPort()); + } + } diff --git a/src/protocolP2P/ProtocolP2PPacketUDP.java b/src/protocolP2P/ProtocolP2PPacketUDP.java index aea6dcc..db92d2f 100644 --- a/src/protocolP2P/ProtocolP2PPacketUDP.java +++ b/src/protocolP2P/ProtocolP2PPacketUDP.java @@ -12,6 +12,7 @@ import remoteException.ProtocolRemoteError; import remoteException.VersionRemoteError; import remoteException.EmptyFile; import tools.BytesArrayTools; +import tools.HostItem; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import protocolP2P.LoadRequest; @@ -353,7 +354,6 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { * @param packet full packet * @throws TransmissionError */ - private void checkCheckSum(byte [] packet) throws TransmissionError { try { int checksum = BytesArrayTools.readInt16Bits(packet, CHECKSUM_POSITION); @@ -365,4 +365,15 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { throw new TransmissionError(); } } + + /** Get hostItem of the sender + * @return hostItem of the sender + * @throws InternalError + */ + public HostItem getHostItem() throws InternalError { + if (responseSocket == null) { + throw new InternalError(); + } + return new HostItem(responseSocket.getInetAddress().getHostName(), responseSocket.getPort()); + } } diff --git a/src/protocolP2P/RequestResponseCode.java b/src/protocolP2P/RequestResponseCode.java index a6e68d3..f216d8c 100644 --- a/src/protocolP2P/RequestResponseCode.java +++ b/src/protocolP2P/RequestResponseCode.java @@ -15,15 +15,20 @@ public enum RequestResponseCode { LIST_REQUEST(CodeType.REQUEST, (byte)0x00), LOAD_REQUEST(CodeType.REQUEST, (byte)0x01), HASH_REQUEST(CodeType.REQUEST, (byte)0x02), + DISCOVER_REQUEST(CodeType.REQUEST_TRACKER, (byte)0x03), + REGISTER(CodeType.REQUEST_TRACKER, (byte)0x04), + UNREGISTER(CodeType.REQUEST_TRACKER, (byte)0x05), LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80), LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81), HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82), + DISCOVER_RESPONSE(CodeType.RESPONSE, (byte)0x83), VERSION_ERROR(CodeType.ERROR, (byte)0xC0), PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1), INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2), EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3), NOT_FOUND(CodeType.ERROR, (byte)0xC4), - EMPTY_FILE(CodeType.ERROR, (byte)0xC5); + EMPTY_FILE(CodeType.ERROR, (byte)0xC5), + NOT_A_TRACKER(CodeType.ERROR, (byte)0xC6); public final CodeType codeType; public final byte codeValue; diff --git a/src/remoteException/NotATracker.java b/src/remoteException/NotATracker.java new file mode 100644 index 0000000..dbccea2 --- /dev/null +++ b/src/remoteException/NotATracker.java @@ -0,0 +1,7 @@ +package remoteException; + +import exception.RemoteException; + +public class NotATracker extends exception.RemoteException { + private static final long serialVersionUID = 12L; +} diff --git a/src/serverP2P/ServerManagementTCP.java b/src/serverP2P/ServerManagementTCP.java index 257fe3d..316143e 100644 --- a/src/serverP2P/ServerManagementTCP.java +++ b/src/serverP2P/ServerManagementTCP.java @@ -136,6 +136,18 @@ public class ServerManagementTCP implements Runnable { logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action); sendHashResponse(pd); break; + case DISCOVER_REQUEST: + logger.writeTCP(addr + "DISCOVER_REQUEST", LogLevel.Action); + sendNotATracker(pd); + break; + case UNREGISTER: + logger.writeTCP(addr + "UNREGISTER", LogLevel.Action); + sendNotATracker(pd); + break; + case REGISTER: + logger.writeTCP(addr + "REGISTER", LogLevel.Action); + sendNotATracker(pd); + break; default: logger.writeTCP(addr + "Received grabbage", LogLevel.Action); sendInternalError(pd); @@ -198,7 +210,18 @@ public class ServerManagementTCP implements Runnable { logger.writeTCP(e, LogLevel.Error); } } - + + /** Send a NotATracker error message. + * @param pd ProtocolP2PPacketTCP to respond + */ + private void sendNotATracker(ProtocolP2PPacketTCP pd) { + try { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_A_TRACKER))); + } catch (Exception e) { + logger.writeTCP(e, LogLevel.Error); + } + } + /** Send response to list request * @param pd Request received */ diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index 986e8d0..3289796 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -89,6 +89,12 @@ public class ServerManagementUDP implements Runnable { logger.writeUDP("Received HASH_REQUEST", LogLevel.Action); sendHashResponse(pd); break; + case DISCOVER_REQUEST: + case UNREGISTER: + case REGISTER: + logger.writeUDP("Received Tracker request", LogLevel.Action); + sendNotATracker(pd); + break; default: sendInternalError(pd); } @@ -213,6 +219,17 @@ public class ServerManagementUDP implements Runnable { } } + /** Send a NotATracker error message. + * @param pd ProtocolP2PPacketUDP to respond + */ + private void sendNotATracker(ProtocolP2PPacketUDP pd) { + try { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_A_TRACKER))); + } catch (Exception e) { + logger.writeUDP(e, LogLevel.Error); + } + } + /** Send hash response to hash request * @param pd Request received */ diff --git a/src/tools/BytesArrayTools.java b/src/tools/BytesArrayTools.java index 8f80fcb..f1cbe85 100644 --- a/src/tools/BytesArrayTools.java +++ b/src/tools/BytesArrayTools.java @@ -219,6 +219,37 @@ public class BytesArrayTools { } } + /** Read string from byte array + * @param array Byte array to read + * @param start start position in byte array + * @param endStr End string delimiter + * @return String read + * @throws InternalError + */ + public static String readString(byte[] array, int start, String endStr) throws InternalError { + boolean failed = false; + try { + int i = start; + while(true) { + for(byte b: endStr.getBytes()) { + if (b != array[i]) { + failed = true; + break; + } + } + if (failed) { + i++; + failed = false; + } else { + break; + } + } + return readString(array, start, i -1 - start); + } catch(IndexOutOfBoundsException e) { + throw new InternalError(); + } + } + /** Write byte Array to byte Array. * @param dst Destination byte Array * @param src Source byte Array diff --git a/src/tools/HostItem.java b/src/tools/HostItem.java index ffe2f58..9bba17b 100644 --- a/src/tools/HostItem.java +++ b/src/tools/HostItem.java @@ -79,10 +79,24 @@ public class HostItem { } return udpSocket; } + + /** Closes udp socket + */ public void closeUDPSocket() { if (udpSocket != null) { udpSocket.close(); } udpSocket = null; } + + /** Getter for hostname */ + public String getHostname() { + return hostname; + } + + /** Getter for port */ + public int getPort() { + return port; + } + }