Merge branch 'hashsum' of flavien/Projet_JAVA_P2P_STRI2A into master
closes #15
This commit is contained in:
commit
7156f6df98
@ -16,10 +16,12 @@ x bytes: [(bytes 8-?): PAYLOAD]
|
|||||||
- REQUESTS (msb is 0):
|
- REQUESTS (msb is 0):
|
||||||
- `LIST` (0x00)
|
- `LIST` (0x00)
|
||||||
- `LOAD` (0x01)
|
- `LOAD` (0x01)
|
||||||
|
- `HASH` (0x02)
|
||||||
|
|
||||||
- RESPONSES (msb is 1):
|
- RESPONSES (msb is 1):
|
||||||
- `LIST` (0x80)
|
- `LIST` (0x80)
|
||||||
- `LOAD` (0x81)
|
- `LOAD` (0x81)
|
||||||
|
- `HASH` (0x82)
|
||||||
- `VERSION ERROR` (0xC0)
|
- `VERSION ERROR` (0xC0)
|
||||||
- `PROTOCOL ERROR` (0xC1)
|
- `PROTOCOL ERROR` (0xC1)
|
||||||
- `INTERNAL ERROR` (0xC2)
|
- `INTERNAL ERROR` (0xC2)
|
||||||
@ -63,6 +65,39 @@ Payload contains
|
|||||||
y bytes: [<FILENAME>]
|
y bytes: [<FILENAME>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Hash
|
||||||
|
#### Hash request
|
||||||
|
Get hash of a file. Payload contains
|
||||||
|
|
||||||
|
```
|
||||||
|
4 bytes: [(bytes 8-11): FILENAME SIZE]
|
||||||
|
y bytes: [<FILENAME>]
|
||||||
|
z bytes: [ALGO_NAMES requested separated by \n] (ex.: SHA-256, MD5)
|
||||||
|
```
|
||||||
|
|
||||||
|
If file does not exists, a NotFound can be responded.
|
||||||
|
|
||||||
|
#### Hash response
|
||||||
|
Payload contains:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
4 bytes: [(bytes 8-11): FILENAME SIZE]
|
||||||
|
y bytes: [<FILENAME>]
|
||||||
|
[[ multiple algo hashes bloc]]
|
||||||
|
```
|
||||||
|
|
||||||
|
A algo hash bloc contains:
|
||||||
|
|
||||||
|
```
|
||||||
|
4 bytes [ALGO_NAME size]
|
||||||
|
? [ALGO_NAME]
|
||||||
|
4 bytes: [HASH SIZE (bytes)] / or 0 if this hash algorithm is unsupported.
|
||||||
|
?? [HASH]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Other response code (errors)
|
### Other response code (errors)
|
||||||
#### Version error
|
#### Version error
|
||||||
Response when datagram received use wrong version code.
|
Response when datagram received use wrong version code.
|
||||||
|
@ -21,12 +21,18 @@ import java.nio.file.Files;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Arrays;
|
||||||
import protocolP2P.ProtocolP2PPacketTCP;
|
import protocolP2P.ProtocolP2PPacketTCP;
|
||||||
import protocolP2P.Payload;
|
import protocolP2P.Payload;
|
||||||
import protocolP2P.RequestResponseCode;
|
import protocolP2P.RequestResponseCode;
|
||||||
import protocolP2P.FileList;
|
import protocolP2P.FileList;
|
||||||
import protocolP2P.FilePart;
|
import protocolP2P.FilePart;
|
||||||
import protocolP2P.LoadRequest;
|
import protocolP2P.LoadRequest;
|
||||||
|
import protocolP2P.HashAlgorithm;
|
||||||
|
import protocolP2P.HashRequest;
|
||||||
|
import protocolP2P.HashResponse;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
||||||
* @author Louis Royer
|
* @author Louis Royer
|
||||||
@ -134,8 +140,30 @@ public class ClientManagementTCP implements Runnable {
|
|||||||
* @throws EmptyFile
|
* @throws EmptyFile
|
||||||
*/
|
*/
|
||||||
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, SocketClosed, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, SocketClosed, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
||||||
|
byte [] hash512;
|
||||||
final long MAX_PARTIAL_SIZE = 4096;
|
final long MAX_PARTIAL_SIZE = 4096;
|
||||||
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
|
||||||
|
hashesAlgo[0] = HashAlgorithm.SHA512;
|
||||||
|
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new HashRequest(filename, hashesAlgo));
|
||||||
|
d.sendRequest((Object)socket);
|
||||||
|
try {
|
||||||
|
Payload pHash = d.receiveResponse().getPayload();
|
||||||
|
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
|
||||||
|
if (!(pHash instanceof HashResponse)) {
|
||||||
|
throw new InternalError();
|
||||||
|
} else {
|
||||||
|
hash512 = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
|
||||||
|
if (hash512.length == 0) {
|
||||||
|
System.err.println("Error: server do not support sha512 hashes");
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (EmptyDirectory e) {
|
||||||
|
System.err.println("Error: empty directory, but request was not LIST_REQUEST");
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
|
||||||
|
d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
||||||
d.sendRequest((Object)socket);
|
d.sendRequest((Object)socket);
|
||||||
boolean fileFullyWritten = false;
|
boolean fileFullyWritten = false;
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
@ -199,6 +227,10 @@ public class ClientManagementTCP implements Runnable {
|
|||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
}
|
}
|
||||||
} while(!fileFullyWritten);
|
} while(!fileFullyWritten);
|
||||||
|
if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) {
|
||||||
|
System.err.println("Error: Hashsum does not match");
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,4 +266,20 @@ public class ClientManagementTCP implements Runnable {
|
|||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute Hashsum of a file.
|
||||||
|
* @param filename
|
||||||
|
* @return hashsum
|
||||||
|
*/
|
||||||
|
private byte[] computeHashsum(String filename, HashAlgorithm h) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
||||||
|
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("Error: " + h.getName() + " not supported");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Error: cannot read " + filename);
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,18 @@ import java.nio.file.Files;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Arrays;
|
||||||
import protocolP2P.ProtocolP2PPacketUDP;
|
import protocolP2P.ProtocolP2PPacketUDP;
|
||||||
import protocolP2P.Payload;
|
import protocolP2P.Payload;
|
||||||
import protocolP2P.RequestResponseCode;
|
import protocolP2P.RequestResponseCode;
|
||||||
import protocolP2P.FileList;
|
import protocolP2P.FileList;
|
||||||
import protocolP2P.FilePart;
|
import protocolP2P.FilePart;
|
||||||
import protocolP2P.LoadRequest;
|
import protocolP2P.LoadRequest;
|
||||||
|
import protocolP2P.HashAlgorithm;
|
||||||
|
import protocolP2P.HashRequest;
|
||||||
|
import protocolP2P.HashResponse;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
||||||
* @author Louis Royer
|
* @author Louis Royer
|
||||||
@ -121,8 +127,30 @@ public class ClientManagementUDP implements Runnable {
|
|||||||
* @throws EmptyFile
|
* @throws EmptyFile
|
||||||
*/
|
*/
|
||||||
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
||||||
|
byte[] hash512;
|
||||||
final long MAX_PARTIAL_SIZE = 4096;
|
final long MAX_PARTIAL_SIZE = 4096;
|
||||||
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
|
||||||
|
hashesAlgo[0] = HashAlgorithm.SHA512;
|
||||||
|
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new HashRequest(filename, hashesAlgo));
|
||||||
|
d.sendRequest((Object)socket);
|
||||||
|
try {
|
||||||
|
Payload pHash = d.receiveResponse().getPayload();
|
||||||
|
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
|
||||||
|
if (!(pHash instanceof HashResponse)) {
|
||||||
|
throw new InternalError();
|
||||||
|
} else {
|
||||||
|
hash512 = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
|
||||||
|
if (hash512.length == 0) {
|
||||||
|
System.err.println("Error: server do not support sha512 hashes");
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (EmptyDirectory e) {
|
||||||
|
System.err.println("Error: empty directory, but request was not LIST_REQUEST");
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
|
||||||
|
d = new ProtocolP2PPacketUDP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
||||||
d.sendRequest((Object)socket);
|
d.sendRequest((Object)socket);
|
||||||
boolean fileFullyWritten = false;
|
boolean fileFullyWritten = false;
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
@ -186,6 +214,10 @@ public class ClientManagementUDP implements Runnable {
|
|||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
}
|
}
|
||||||
} while(!fileFullyWritten);
|
} while(!fileFullyWritten);
|
||||||
|
if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) {
|
||||||
|
System.err.println("Error: Hashsum does not match");
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** list server’s directory content
|
/** list server’s directory content
|
||||||
@ -219,4 +251,20 @@ public class ClientManagementUDP implements Runnable {
|
|||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute Hashsum of a file.
|
||||||
|
* @param filename
|
||||||
|
* @return hashsum
|
||||||
|
*/
|
||||||
|
private byte[] computeHashsum(String filename, HashAlgorithm h) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
||||||
|
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("Error: " + h.getName() + " not supported");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Error: cannot read " + filename);
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
src/protocolP2P/HashAlgorithm.java
Normal file
52
src/protocolP2P/HashAlgorithm.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import tools.BytesArrayTools;
|
||||||
|
|
||||||
|
/** HashAlgorithm enum.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public enum HashAlgorithm {
|
||||||
|
SHA512("SHA-512"),
|
||||||
|
MD5("MD5");
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
/* To be able to convert name to enum */
|
||||||
|
private static final Map<String, HashAlgorithm> BY_NAME = new HashMap<>();
|
||||||
|
/* Initialization of HashMap */
|
||||||
|
static {
|
||||||
|
for (HashAlgorithm h: values()) {
|
||||||
|
assert !BY_NAME.containsKey(h.name) : "Duplicate in " + HashAlgorithm.class.getCanonicalName();
|
||||||
|
BY_NAME.put(h.name, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashAlgorithm(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gives enum from name.
|
||||||
|
* @param name name of the hash
|
||||||
|
* @return enum element
|
||||||
|
* @throws InterlanError
|
||||||
|
*/
|
||||||
|
protected static HashAlgorithm fromName(String name) throws InternalError {
|
||||||
|
HashAlgorithm h = BY_NAME.get(BytesArrayTools.cleanStrings(name));
|
||||||
|
assert h != null : "Bad algorithm name: " + name;
|
||||||
|
if (h == null) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
165
src/protocolP2P/HashRequest.java
Normal file
165
src/protocolP2P/HashRequest.java
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.HashAlgorithm;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import tools.BytesArrayTools;
|
||||||
|
|
||||||
|
|
||||||
|
/** Representation of payload for hash request.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HashRequest extends Payload {
|
||||||
|
private String filename;
|
||||||
|
private HashAlgorithm[] algoList;
|
||||||
|
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
|
||||||
|
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
|
||||||
|
|
||||||
|
/** Constructor (typically used by the server) with a filename parameter.
|
||||||
|
* @param filename name of the file to download. Must not be empty.
|
||||||
|
* @param algoList List of hash algorithms used
|
||||||
|
* @throws InternalError
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public HashRequest(String filename, HashAlgorithm[] algoList) throws InternalError {
|
||||||
|
super(RequestResponseCode.HASH_REQUEST);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert filename.length() != 0 : "Size of filename in HashRequest must not be empty";
|
||||||
|
if (filename.length() == 0) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.filename = filename;
|
||||||
|
assert algoList.length != 0 : "Hash algorithms list must not be empty";
|
||||||
|
if (algoList.length == 0) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.algoList = algoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
|
||||||
|
* @param packet the full Packet received
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws TransmissionError
|
||||||
|
*/
|
||||||
|
protected HashRequest(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||||
|
super(packet);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert requestResponseCode == RequestResponseCode.HASH_REQUEST : "HashRequest subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
|
||||||
|
/* InternalErrorException */
|
||||||
|
if (requestResponseCode!= RequestResponseCode.HASH_REQUEST) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
/* Read filename */
|
||||||
|
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
|
||||||
|
try {
|
||||||
|
filename = new String(packet, FILENAME_POSITION, filenameSize, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = getPayloadSize(packet);
|
||||||
|
try {
|
||||||
|
String[] l = (new String(packet, FILENAME_POSITION + filenameSize, size, "UTF-8")).split("\n");
|
||||||
|
int i = 0;
|
||||||
|
algoList = new HashAlgorithm[l.length];
|
||||||
|
for(String algo : l) {
|
||||||
|
algoList[i] = HashAlgorithm.fromName(algo);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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 size
|
||||||
|
int filenameSize = filename.length();
|
||||||
|
int size = FILENAME_POSITION + filenameSize;
|
||||||
|
for (HashAlgorithm h : algoList) {
|
||||||
|
if (h == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size += h.getName().length();
|
||||||
|
size += 1; // size for '\n'
|
||||||
|
}
|
||||||
|
size -=1; // remove trailing '\n'
|
||||||
|
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 - FILENAME_SIZE_POSITION, packet);
|
||||||
|
|
||||||
|
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
|
||||||
|
|
||||||
|
// Write filename
|
||||||
|
int bCount = FILENAME_POSITION;
|
||||||
|
try {
|
||||||
|
byte[] sb = filename.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
packet[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
boolean firstIter = true;
|
||||||
|
for(HashAlgorithm h : algoList) {
|
||||||
|
if (h == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String s = h.getName();
|
||||||
|
if (!firstIter) {
|
||||||
|
firstIter = false;
|
||||||
|
try {
|
||||||
|
packet[bCount] = "\n".getBytes("UTF-8")[0]; // separator
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
// Copy algoname
|
||||||
|
try {
|
||||||
|
byte[] sb = s.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
packet[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** AlgoList getter.
|
||||||
|
* @return List of HashAlgorithms
|
||||||
|
*/
|
||||||
|
public HashAlgorithm[] getAlgoList() {
|
||||||
|
return algoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filename getter.
|
||||||
|
* @return filename
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
185
src/protocolP2P/HashResponse.java
Normal file
185
src/protocolP2P/HashResponse.java
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import tools.BytesArrayTools;
|
||||||
|
|
||||||
|
|
||||||
|
/** Representation of payload for hash response.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HashResponse extends Payload {
|
||||||
|
private String filename;
|
||||||
|
private Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
||||||
|
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
|
||||||
|
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
|
||||||
|
|
||||||
|
/** Constructor (typically used by the server) with a filename parameter.
|
||||||
|
* @param filename name of the file to download. Must not be empty.
|
||||||
|
* @param hashes HashMap containing hashes for file.
|
||||||
|
* @throws InternalError
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public HashResponse(String filename, Map<HashAlgorithm, byte[]> hashes) throws InternalError {
|
||||||
|
super(RequestResponseCode.HASH_RESPONSE);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert filename.length() != 0 : "Size of filename in HashResponse must not be empty";
|
||||||
|
if (filename.length() == 0) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.filename = filename;
|
||||||
|
|
||||||
|
// initialize hashes
|
||||||
|
assert !hashes.isEmpty() : "At least 1 hash must be provided";
|
||||||
|
if (hashes.isEmpty()) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.hashes = hashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
|
||||||
|
* @param packet the full Packet received
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws TransmissionError
|
||||||
|
*/
|
||||||
|
protected HashResponse(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||||
|
super(packet);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert requestResponseCode == RequestResponseCode.HASH_RESPONSE : "HashResponse subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
|
||||||
|
/* InternalErrorException */
|
||||||
|
if (requestResponseCode!= RequestResponseCode.HASH_RESPONSE) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
/* Read filename */
|
||||||
|
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
|
||||||
|
try {
|
||||||
|
filename = new String(packet, FILENAME_POSITION, filenameSize, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = getPayloadSize(packet);
|
||||||
|
int start = FILENAME_POSITION + filenameSize;
|
||||||
|
do {
|
||||||
|
int algoNameSize = BytesArrayTools.readInt(packet, start);
|
||||||
|
start +=4;
|
||||||
|
try {
|
||||||
|
String algoName = new String(packet, start, algoNameSize, "UTF-8");
|
||||||
|
start += algoNameSize;
|
||||||
|
int hashSize = BytesArrayTools.readInt(packet, start);
|
||||||
|
start += 4;
|
||||||
|
if (hashSize != 0) {
|
||||||
|
byte[] b = new byte[hashSize];
|
||||||
|
for (int i=0;i<hashSize;i++) {
|
||||||
|
b[i] = packet[start+i];
|
||||||
|
}
|
||||||
|
hashes.put(HashAlgorithm.fromName(algoName), b);
|
||||||
|
}
|
||||||
|
start += hashSize;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
} while (start < 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 size
|
||||||
|
int filenameSize = filename.length();
|
||||||
|
int size = FILENAME_POSITION + filenameSize;
|
||||||
|
for (HashAlgorithm h : hashes.keySet()) {
|
||||||
|
size += 4 + h.getName().length();
|
||||||
|
}
|
||||||
|
for (byte[] s : hashes.values()) {
|
||||||
|
size += 4 + s.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 - FILENAME_SIZE_POSITION, packet);
|
||||||
|
|
||||||
|
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
|
||||||
|
|
||||||
|
// Write filename
|
||||||
|
int bCount = FILENAME_POSITION;
|
||||||
|
try {
|
||||||
|
byte[] sb = filename.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
packet[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
for(HashAlgorithm h : hashes.keySet()) {
|
||||||
|
String s = h.getName();
|
||||||
|
BytesArrayTools.write(packet, bCount, (int)s.length());
|
||||||
|
bCount += 4;
|
||||||
|
// Copy algoname
|
||||||
|
try {
|
||||||
|
byte[] sb = s.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
packet[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
byte[] hashb = hashes.get(HashAlgorithm.fromName(s));
|
||||||
|
if (hashb.length == 0) {
|
||||||
|
BytesArrayTools.write(packet, bCount, (int)0);
|
||||||
|
bCount += 4;
|
||||||
|
} else {
|
||||||
|
BytesArrayTools.write(packet, bCount, (int)hashb.length);
|
||||||
|
bCount +=4;
|
||||||
|
// copy hash
|
||||||
|
for(byte b : hashb) {
|
||||||
|
packet[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** hash getter
|
||||||
|
* @param hashAlgo HashAlgorithm to return hash from
|
||||||
|
* @return hash
|
||||||
|
*/
|
||||||
|
public byte[] getHash(HashAlgorithm hashAlgo) {
|
||||||
|
byte[] b = hashes.get(hashAlgo);
|
||||||
|
if (b == null) {
|
||||||
|
return new byte[0];
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** filename getter.
|
||||||
|
* @return filename
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,8 @@ import protocolP2P.RequestResponseCode;
|
|||||||
import protocolP2P.FilePart;
|
import protocolP2P.FilePart;
|
||||||
import protocolP2P.FileList;
|
import protocolP2P.FileList;
|
||||||
import protocolP2P.LoadRequest;
|
import protocolP2P.LoadRequest;
|
||||||
|
import protocolP2P.HashRequest;
|
||||||
|
import protocolP2P.HashResponse;
|
||||||
import exception.ProtocolError;
|
import exception.ProtocolError;
|
||||||
import exception.InternalError;
|
import exception.InternalError;
|
||||||
import exception.TransmissionError;
|
import exception.TransmissionError;
|
||||||
@ -28,6 +30,8 @@ public class Payload {
|
|||||||
assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart 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_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.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";
|
||||||
this.requestResponseCode = requestResponseCode;
|
this.requestResponseCode = requestResponseCode;
|
||||||
checkRequestResponseCode(); // this can throw InternalError
|
checkRequestResponseCode(); // this can throw InternalError
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
|
|||||||
case EMPTY_FILE:
|
case EMPTY_FILE:
|
||||||
case LOAD_RESPONSE:
|
case LOAD_RESPONSE:
|
||||||
case LIST_RESPONSE:
|
case LIST_RESPONSE:
|
||||||
|
case HASH_RESPONSE:
|
||||||
// we were expecting a request, but we are receiving a response
|
// we were expecting a request, but we are receiving a response
|
||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
default :
|
default :
|
||||||
@ -281,6 +282,12 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
|
|||||||
case LOAD_REQUEST:
|
case LOAD_REQUEST:
|
||||||
payload = (Payload) new LoadRequest(packet);
|
payload = (Payload) new LoadRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
case HASH_REQUEST:
|
||||||
|
payload = (Payload) new HashRequest(packet);
|
||||||
|
break;
|
||||||
|
case HASH_RESPONSE:
|
||||||
|
payload = (Payload) new HashResponse(packet);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
payload = new Payload(packet); // this can throw TransmissionError
|
payload = new Payload(packet); // this can throw TransmissionError
|
||||||
break;
|
break;
|
||||||
|
@ -129,6 +129,7 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
|
|||||||
case EMPTY_FILE:
|
case EMPTY_FILE:
|
||||||
case LOAD_RESPONSE:
|
case LOAD_RESPONSE:
|
||||||
case LIST_RESPONSE:
|
case LIST_RESPONSE:
|
||||||
|
case HASH_RESPONSE:
|
||||||
// we were expecting a request, but we are receiving a response
|
// we were expecting a request, but we are receiving a response
|
||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
default :
|
default :
|
||||||
@ -277,6 +278,12 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
|
|||||||
case LOAD_REQUEST:
|
case LOAD_REQUEST:
|
||||||
payload = (Payload) new LoadRequest(packet);
|
payload = (Payload) new LoadRequest(packet);
|
||||||
break;
|
break;
|
||||||
|
case HASH_REQUEST:
|
||||||
|
payload = (Payload) new HashRequest(packet);
|
||||||
|
break;
|
||||||
|
case HASH_RESPONSE:
|
||||||
|
payload = (Payload) new HashResponse(packet);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
payload = new Payload(packet); // this can throw TransmissionError
|
payload = new Payload(packet); // this can throw TransmissionError
|
||||||
break;
|
break;
|
||||||
|
@ -14,8 +14,10 @@ import java.lang.Byte;
|
|||||||
public enum RequestResponseCode {
|
public enum RequestResponseCode {
|
||||||
LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
|
LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
|
||||||
LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
|
LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
|
||||||
|
HASH_REQUEST(CodeType.REQUEST, (byte)0x02),
|
||||||
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
|
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
|
||||||
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
|
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
|
||||||
|
HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82),
|
||||||
VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
|
VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
|
||||||
PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
|
PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
|
||||||
INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
|
INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
|
||||||
@ -49,10 +51,11 @@ public enum RequestResponseCode {
|
|||||||
|
|
||||||
/** Gives enum from Packet code.
|
/** Gives enum from Packet code.
|
||||||
* @param code value of the element in packet
|
* @param code value of the element in packet
|
||||||
* @return enum element
|
* @return enum element
|
||||||
|
* @throws ProtocolError
|
||||||
*/
|
*/
|
||||||
protected static RequestResponseCode fromCode(byte code) throws ProtocolError {
|
protected static RequestResponseCode fromCode(byte code) throws ProtocolError {
|
||||||
RequestResponseCode r= BY_CODE.get(Byte.valueOf(code));
|
RequestResponseCode r = BY_CODE.get(Byte.valueOf(code));
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
throw new ProtocolError();
|
throw new ProtocolError();
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import protocolP2P.ProtocolP2PPacketTCP;
|
import protocolP2P.ProtocolP2PPacketTCP;
|
||||||
@ -30,6 +32,11 @@ import remoteException.EmptyFile;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import tools.Logger;
|
import tools.Logger;
|
||||||
import tools.LogLevel;
|
import tools.LogLevel;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import protocolP2P.HashAlgorithm;
|
||||||
|
import protocolP2P.HashRequest;
|
||||||
|
import protocolP2P.HashResponse;
|
||||||
|
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
|
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
|
||||||
@ -41,6 +48,7 @@ import tools.LogLevel;
|
|||||||
public class ServerManagementTCP implements Runnable {
|
public class ServerManagementTCP implements Runnable {
|
||||||
|
|
||||||
private String[] fileList;
|
private String[] fileList;
|
||||||
|
private Map<String, byte[]> sha512 = new HashMap<>();
|
||||||
private String baseDirectory;
|
private String baseDirectory;
|
||||||
private int TCPPort;
|
private int TCPPort;
|
||||||
private ServerSocket socket;
|
private ServerSocket socket;
|
||||||
@ -55,6 +63,7 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
this.TCPPort = TCPPort;
|
this.TCPPort = TCPPort;
|
||||||
initFileList();
|
initFileList();
|
||||||
|
initSha512();
|
||||||
try {
|
try {
|
||||||
socket = new ServerSocket(TCPPort);
|
socket = new ServerSocket(TCPPort);
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
@ -123,6 +132,10 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
logger.writeTCP(addr + "LIST_REQUEST", LogLevel.Action);
|
logger.writeTCP(addr + "LIST_REQUEST", LogLevel.Action);
|
||||||
sendListResponse(pd);
|
sendListResponse(pd);
|
||||||
break;
|
break;
|
||||||
|
case HASH_REQUEST:
|
||||||
|
logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action);
|
||||||
|
sendHashResponse(pd);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logger.writeTCP(addr + "Received grabbage", LogLevel.Action);
|
logger.writeTCP(addr + "Received grabbage", LogLevel.Action);
|
||||||
sendInternalError(pd);
|
sendInternalError(pd);
|
||||||
@ -158,6 +171,22 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
Arrays.sort(fileList);
|
Arrays.sort(fileList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Init sha512 map.
|
||||||
|
*/
|
||||||
|
private void initSha512() {
|
||||||
|
for(String f: fileList) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
||||||
|
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
|
||||||
|
md.reset();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
logger.writeTCP("sha512 not supported", LogLevel.Error);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.writeTCP("cannot read " + f, LogLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Send an internal error message.
|
/** Send an internal error message.
|
||||||
* @param pd ProtocolP2PPacketTCP to respond
|
* @param pd ProtocolP2PPacketTCP to respond
|
||||||
*/
|
*/
|
||||||
@ -187,6 +216,46 @@ public class ServerManagementTCP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Send hash response to hash request
|
||||||
|
* @param pd Request received
|
||||||
|
*/
|
||||||
|
private void sendHashResponse(ProtocolP2PPacketTCP pd) {
|
||||||
|
Payload p = pd.getPayload();
|
||||||
|
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
|
||||||
|
if (!(p instanceof HashRequest)) {
|
||||||
|
sendInternalError(pd);
|
||||||
|
} else {
|
||||||
|
String filename = ((HashRequest)p).getFilename();
|
||||||
|
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
||||||
|
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
||||||
|
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
|
||||||
|
switch (h) {
|
||||||
|
case SHA512:
|
||||||
|
hashes.put(h, sha512.get(filename));
|
||||||
|
break;
|
||||||
|
case MD5:
|
||||||
|
default:
|
||||||
|
hashes.put(h, new byte[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new HashResponse(filename, hashes))));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.writeTCP(e, LogLevel.Error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// file not found
|
||||||
|
try {
|
||||||
|
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.writeTCP(e, LogLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Send response to load request
|
/** Send response to load request
|
||||||
* @param pd Request received
|
* @param pd Request received
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import protocolP2P.ProtocolP2PPacketUDP;
|
import protocolP2P.ProtocolP2PPacketUDP;
|
||||||
import protocolP2P.ProtocolP2PPacket;
|
import protocolP2P.ProtocolP2PPacket;
|
||||||
import protocolP2P.RequestResponseCode;
|
import protocolP2P.RequestResponseCode;
|
||||||
@ -29,7 +31,11 @@ import remoteException.EmptyFile;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import tools.Logger;
|
import tools.Logger;
|
||||||
import tools.LogLevel;
|
import tools.LogLevel;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import protocolP2P.HashAlgorithm;
|
||||||
|
import protocolP2P.HashRequest;
|
||||||
|
import protocolP2P.HashResponse;
|
||||||
|
|
||||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
||||||
* @author Louis Royer
|
* @author Louis Royer
|
||||||
@ -40,6 +46,7 @@ import tools.LogLevel;
|
|||||||
public class ServerManagementUDP implements Runnable {
|
public class ServerManagementUDP implements Runnable {
|
||||||
|
|
||||||
private String[] fileList;
|
private String[] fileList;
|
||||||
|
private Map<String, byte[]> sha512 = new HashMap<>();
|
||||||
private String baseDirectory;
|
private String baseDirectory;
|
||||||
private int UDPPort;
|
private int UDPPort;
|
||||||
private DatagramSocket socket;
|
private DatagramSocket socket;
|
||||||
@ -54,6 +61,7 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
this.baseDirectory = baseDirectory;
|
this.baseDirectory = baseDirectory;
|
||||||
this.UDPPort = UDPPort;
|
this.UDPPort = UDPPort;
|
||||||
initFileList();
|
initFileList();
|
||||||
|
initSha512();
|
||||||
try {
|
try {
|
||||||
socket = new DatagramSocket(UDPPort);
|
socket = new DatagramSocket(UDPPort);
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
@ -134,6 +142,10 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
logger.writeUDP(e2, LogLevel.Error);
|
logger.writeUDP(e2, LogLevel.Error);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case HASH_REQUEST:
|
||||||
|
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
|
||||||
|
sendHashResponse(pd);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
sendInternalError(pd);
|
sendInternalError(pd);
|
||||||
}
|
}
|
||||||
@ -165,7 +177,21 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
Arrays.sort(fileList);
|
Arrays.sort(fileList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Init sha512 map.
|
||||||
|
*/
|
||||||
|
private void initSha512() {
|
||||||
|
for(String f: fileList) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
||||||
|
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
|
||||||
|
md.reset();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
logger.writeUDP("sha512 not supported", LogLevel.Error);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.writeUDP("cannot read " + f, LogLevel.Warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Send an internal error message.
|
/** Send an internal error message.
|
||||||
* @param pd ProtocolP2PPacketUDP to respond
|
* @param pd ProtocolP2PPacketUDP to respond
|
||||||
@ -179,5 +205,44 @@ public class ServerManagementUDP implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Send hash response to hash request
|
||||||
|
* @param pd Request received
|
||||||
|
*/
|
||||||
|
private void sendHashResponse(ProtocolP2PPacketUDP pd) {
|
||||||
|
Payload p = pd.getPayload();
|
||||||
|
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
|
||||||
|
if (!(p instanceof HashRequest)) {
|
||||||
|
sendInternalError(pd);
|
||||||
|
} else {
|
||||||
|
String filename = ((HashRequest)p).getFilename();
|
||||||
|
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
||||||
|
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
|
||||||
|
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
|
||||||
|
switch (h) {
|
||||||
|
case SHA512:
|
||||||
|
hashes.put(h, sha512.get(filename));
|
||||||
|
break;
|
||||||
|
case MD5:
|
||||||
|
default:
|
||||||
|
hashes.put(h, new byte[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new HashResponse(filename, hashes))));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.writeUDP(e, LogLevel.Error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// file not found
|
||||||
|
try {
|
||||||
|
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.writeUDP(e, LogLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,4 +100,20 @@ public class BytesArrayTools {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remove trailing null bytes from string.
|
||||||
|
* @param str input string
|
||||||
|
* @return str without trailing null bytes
|
||||||
|
*/
|
||||||
|
public static String cleanStrings(String str) {
|
||||||
|
int i = 0;
|
||||||
|
int cpt = 0;
|
||||||
|
byte[] bArray = str.getBytes();
|
||||||
|
for (byte b : bArray) {
|
||||||
|
if (b != 0) {
|
||||||
|
cpt = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return new String(bArray, 0, cpt + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
package tools;
|
package tools;
|
||||||
|
/** LogLevel Enum.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
public enum LogLevel {
|
public enum LogLevel {
|
||||||
Error,
|
Error,
|
||||||
Info,
|
Info,
|
||||||
|
@ -98,6 +98,7 @@ public class Logger {
|
|||||||
*/
|
*/
|
||||||
public void writeTCP(Exception e, LogLevel logLevel) {
|
public void writeTCP(Exception e, LogLevel logLevel) {
|
||||||
writeTCP(e.toString(), logLevel);
|
writeTCP(e.toString(), logLevel);
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Appends log to filelog and print to stderr.
|
/** Appends log to filelog and print to stderr.
|
||||||
@ -116,6 +117,7 @@ public class Logger {
|
|||||||
*/
|
*/
|
||||||
public void writeUDP(Exception e, LogLevel logLevel) {
|
public void writeUDP(Exception e, LogLevel logLevel) {
|
||||||
writeUDP(e.toString(), logLevel);
|
writeUDP(e.toString(), logLevel);
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user