/** * Handles a specific <tt>IOException</tt> which was thrown during the execution of {@link * #runInConnectThread(DTLSProtocol, TlsPeer, DatagramTransport)} while trying to establish a DTLS * connection * * @param ioe the <tt>IOException</tt> to handle * @param msg the human-readable message to log about the specified <tt>ioe</tt> * @param i the number of tries remaining after the current one * @return <tt>true</tt> if the specified <tt>ioe</tt> was successfully handled; <tt>false</tt>, * otherwise */ private boolean handleRunInConnectThreadException(IOException ioe, String msg, int i) { // SrtpControl.start(MediaType) starts its associated TransformEngine. // We will use that mediaType to signal the normal stop then as well // i.e. we will ignore exception after the procedure to stop this // PacketTransformer has begun. if (mediaType == null) return false; if (ioe instanceof TlsFatalAlert) { TlsFatalAlert tfa = (TlsFatalAlert) ioe; short alertDescription = tfa.getAlertDescription(); if (alertDescription == AlertDescription.unexpected_message) { msg += " Received fatal unexpected message."; if (i == 0 || !Thread.currentThread().equals(connectThread) || connector == null || mediaType == null) { msg += " Giving up after " + (CONNECT_TRIES - i) + " retries."; } else { msg += " Will retry."; logger.error(msg, ioe); return true; } } else { msg += " Received fatal alert " + alertDescription + "."; } } logger.error(msg, ioe); return false; }
/** Stops this <tt>PacketTransformer</tt>. */ private synchronized void stop() { if (connectThread != null) connectThread = null; try { // The dtlsTransport and _srtpTransformer SHOULD be closed, of // course. The datagramTransport MUST be closed. if (dtlsTransport != null) { try { dtlsTransport.close(); } catch (IOException ioe) { logger.error("Failed to (properly) close " + dtlsTransport.getClass(), ioe); } dtlsTransport = null; } if (_srtpTransformer != null) { _srtpTransformer.close(); _srtpTransformer = null; } } finally { try { closeDatagramTransport(); } finally { notifyAll(); } } }
/** * Closes {@link #datagramTransport} if it is non-<tt>null</tt> and logs and swallows any * <tt>IOException</tt>. */ private void closeDatagramTransport() { if (datagramTransport != null) { try { datagramTransport.close(); } catch (IOException ioe) { // DatagramTransportImpl has no reason to fail because it is // merely an adapter of #connector and this PacketTransformer to // the terms of the Bouncy Castle Crypto API. logger.error("Failed to (properly) close " + datagramTransport.getClass(), ioe); } datagramTransport = null; } }
/** * Sends the data contained in a specific byte array as application data through the DTLS * connection of this <tt>DtlsPacketTransformer</tt>. * * @param buf the byte array containing data to send. * @param off the offset in <tt>buf</tt> where the data begins. * @param len the length of data to send. */ public void sendApplicationData(byte[] buf, int off, int len) { DTLSTransport dtlsTransport = this.dtlsTransport; Throwable throwable = null; if (dtlsTransport != null) { try { dtlsTransport.send(buf, off, len); } catch (IOException ioe) { throwable = ioe; } } else { throwable = new NullPointerException("dtlsTransport"); } if (throwable != null) { // SrtpControl.start(MediaType) starts its associated // TransformEngine. We will use that mediaType to signal the normal // stop then as well i.e. we will ignore exception after the // procedure to stop this PacketTransformer has begun. if (mediaType != null && !tlsPeerHasRaisedCloseNotifyWarning) { logger.error("Failed to send application data over DTLS transport: ", throwable); } } }
/** {@inheritDoc} */ @Override public RawPacket reverseTransform(RawPacket pkt) { byte[] buf = pkt.getBuffer(); int off = pkt.getOffset(); int len = pkt.getLength(); if (isDtlsRecord(buf, off, len)) { if (rtcpmux && Component.RTCP == componentID) { // This should never happen. logger.warn( "Dropping a DTLS record, because it was received on the" + " RTCP channel while rtcpmux is in use."); return null; } boolean receive; synchronized (this) { if (datagramTransport == null) { receive = false; } else { datagramTransport.queueReceive(buf, off, len); receive = true; } } if (receive) { DTLSTransport dtlsTransport = this.dtlsTransport; if (dtlsTransport == null) { // The specified pkt looks like a DTLS record and it has // been consumed for the purposes of the secure channel // represented by this PacketTransformer. pkt = null; } else { try { int receiveLimit = dtlsTransport.getReceiveLimit(); int delta = receiveLimit - len; if (delta > 0) { pkt.grow(delta); buf = pkt.getBuffer(); off = pkt.getOffset(); len = pkt.getLength(); } else if (delta < 0) { pkt.shrink(-delta); buf = pkt.getBuffer(); off = pkt.getOffset(); len = pkt.getLength(); } int received = dtlsTransport.receive(buf, off, len, DTLS_TRANSPORT_RECEIVE_WAITMILLIS); if (received <= 0) { // No application data was decoded. pkt = null; } else { delta = len - received; if (delta > 0) pkt.shrink(delta); } } catch (IOException ioe) { pkt = null; // SrtpControl.start(MediaType) starts its associated // TransformEngine. We will use that mediaType to signal // the normal stop then as well i.e. we will ignore // exception after the procedure to stop this // PacketTransformer has begun. if (mediaType != null && !tlsPeerHasRaisedCloseNotifyWarning) { logger.error("Failed to decode a DTLS record!", ioe); } } } } else { // The specified pkt looks like a DTLS record but it is // unexpected in the current state of the secure channel // represented by this PacketTransformer. This PacketTransformer // has not been started (successfully) or has been closed. pkt = null; } } else if (transformEngine.isSrtpDisabled()) { // In pure DTLS mode only DTLS records pass through. pkt = null; } else { // DTLS-SRTP has not been initialized yet or has failed to // initialize. SinglePacketTransformer srtpTransformer = waitInitializeAndGetSRTPTransformer(); if (srtpTransformer != null) pkt = srtpTransformer.reverseTransform(pkt); else if (DROP_UNENCRYPTED_PKTS) pkt = null; // XXX Else, it is our explicit policy to let the received packet // pass through and rely on the SrtpListener to notify the user that // the session is not secured. } return pkt; }