List<Record> records = Record.fromByteArray(data);
for (Record record : records) {
record.setSession(session);
RawData raw = null;
ContentType contentType = record.getType();
LOGGER.finest(" => contentType: "+contentType);
DTLSFlight flight = null;
switch (contentType) {
case APPLICATION_DATA:
if (session == null) {
// There is no session available, so no application data
// should be received, discard it
LOGGER.info("Discarded unexpected application data message from " + peerAddress.toString());
return null;
}
// at this point, the current handshaker is not needed
// anymore, remove it
handshakers.remove(addressToKey(peerAddress));
ApplicationMessage applicationData = (ApplicationMessage) record.getFragment();
raw = new RawData(applicationData.getData());
break;
case ALERT:
AlertMessage alert = (AlertMessage) record.getFragment();
switch (alert.getDescription()) {
case CLOSE_NOTIFY:
session.setActive(false);
LOGGER.fine("Received CLOSE_NOTIFY from " + peerAddress.toString());
// server must reply with CLOSE_NOTIFY
if (!session.isClient()) {
DTLSMessage closeNotify = new AlertMessage(AlertLevel.WARNING, AlertDescription.CLOSE_NOTIFY);
flight = new DTLSFlight();
flight.addMessage(new Record(ContentType.ALERT, session.getWriteEpoch(), session.getSequenceNumber(), closeNotify, session));
flight.setRetransmissionNeeded(false);
}
if (dtlsSessions.remove(addressToKey(peerAddress))!=null) {
LOGGER.info("Closed session with peer: " + peerAddress.toString());
} else {
LOGGER.warning("Session to close not found: " + peerAddress.toString());
}
break;
// remote implementation might use any alert (e.g., against padding oracle attack)
default:
LOGGER.warning(alert.getDescription() + " with " + peerAddress.toString());
// cleaning up
cancelPreviousFlight(peerAddress);
dtlsSessions.remove(addressToKey(peerAddress));
handshakers.remove(addressToKey(peerAddress));
break;
//TODO somehow tell CoAP endpoint to cancel
}
break;
case CHANGE_CIPHER_SPEC:
case HANDSHAKE:
LOGGER.finest(" => handshaker: "+handshaker);
if (handshaker == null) {
/*
* A handshake message received, but no handshaker
* available: this must mean that we either received
* a HelloRequest (from server) or a ClientHello
* (from client) => initialize appropriate
* handshaker type
*/
HandshakeMessage handshake = (HandshakeMessage) record.getFragment();
switch (handshake.getMessageType()) {
case HELLO_REQUEST:
/*
* Client side: server desires a re-handshake
*/
if (session == null) {
// create new session
session = new DTLSSession(peerAddress, true);
// store session according to peer address
dtlsSessions.put(addressToKey(peerAddress), session);
LOGGER.info("Created new session as client with peer: " + peerAddress.toString());
}
handshaker = new ClientHandshaker(peerAddress, null, session);
handshakers.put(addressToKey(peerAddress), handshaker);
LOGGER.finest("Stored re-handshaker: " + handshaker.toString() + " for " + peerAddress.toString());
break;
case CLIENT_HELLO:
/*
* Server side: server received a client hello:
* check first if client wants to resume a
* session (message must contain session
* identifier) and then check if particular
* session still available, otherwise conduct
* full handshake with fresh session.
*/
if (!(handshake instanceof FragmentedHandshakeMessage)) {
// check if session identifier set
ClientHello clientHello = (ClientHello) handshake;
session = getSessionByIdentifier(clientHello.getSessionId().getSessionId());
}
if (session == null) {
// create new session
session = new DTLSSession(peerAddress, false);
// store session according to peer address
dtlsSessions.put(addressToKey(peerAddress), session);
LOGGER.info("Created new session as server with peer: " + peerAddress.toString());
handshaker = new ServerHandshaker(peerAddress, session);
} else {
handshaker = new ResumingServerHandshaker(peerAddress, session);
}
handshakers.put(addressToKey(peerAddress), handshaker);
LOGGER.finest("Stored handshaker: " + handshaker.toString() + " for " + peerAddress.toString());
break;
default:
LOGGER.severe("Received unexpected first handshake message (type="+handshake.getMessageType()+") from " + peerAddress.toString() + ":\n" + handshake.toString());
break;
}
}
flight = handshaker.processMessage(record);
break;
default:
LOGGER.severe("Received unknown DTLS record from " + peerAddress.toString() + ":\n" + ByteArrayUtils.toHexString(data));
break;
}
if (flight != null) {
cancelPreviousFlight(peerAddress);
flight.setPeerAddress(peerAddress);
flight.setSession(session);
if (flight.isRetransmissionNeeded()) {
flights.put(addressToKey(peerAddress), flight);
scheduleRetransmission(flight);
}
sendFlight(flight);
}
if (raw != null) {
raw.setAddress(packet.getAddress());
raw.setPort(packet.getPort());
return raw;
}
}