Exemplo n.º 1
0
  /**
   * {@inheritDoc}
   *
   * <p>SCTP input data callback.
   */
  @Override
  public void onSctpPacket(
      byte[] data, int sid, int ssn, int tsn, long ppid, int context, int flags) {
    if (ppid == WEB_RTC_PPID_CTRL) {
      // Channel control PPID
      try {
        onCtrlPacket(data, sid);
      } catch (IOException e) {
        logger.error("IOException when processing ctrl packet", e);
      }
    } else if (ppid == WEB_RTC_PPID_STRING || ppid == WEB_RTC_PPID_BIN) {
      WebRtcDataStream channel;

      synchronized (this) {
        channel = channels.get(sid);
      }

      if (channel == null) {
        logger.error("No channel found for sid: " + sid);
        return;
      }
      if (ppid == WEB_RTC_PPID_STRING) {
        // WebRTC String
        String str;
        String charsetName = "UTF-8";

        try {
          str = new String(data, charsetName);
        } catch (UnsupportedEncodingException uee) {
          logger.error("Unsupported charset encoding/name " + charsetName, uee);
          str = null;
        }
        channel.onStringMsg(str);
      } else {
        // WebRTC Binary
        channel.onBinaryMsg(data);
      }
    } else {
      logger.warn("Got message on unsupported PPID: " + ppid);
    }
  }
Exemplo n.º 2
0
  /** Starts this <tt>PacketTransformer</tt>. */
  private synchronized void start() {
    if (this.datagramTransport != null) {
      if (this.connectThread == null && dtlsTransport == null) {
        logger.warn(
            getClass().getName()
                + " has been started but has failed to establish"
                + " the DTLS connection!");
      }
      return;
    }

    if (rtcpmux && Component.RTCP == componentID) {
      // In the case of rtcp-mux, the RTCP transformer does not create
      // a DTLS session. The SRTP context (_srtpTransformer) will be
      // initialized on demand using initializeSRTCPTransformerFromRtp().
      return;
    }

    AbstractRTPConnector connector = this.connector;

    if (connector == null) throw new NullPointerException("connector");

    DtlsControl.Setup setup = this.setup;
    SecureRandom secureRandom = DtlsControlImpl.createSecureRandom();
    final DTLSProtocol dtlsProtocolObj;
    final TlsPeer tlsPeer;

    if (DtlsControl.Setup.ACTIVE.equals(setup)) {
      dtlsProtocolObj = new DTLSClientProtocol(secureRandom);
      tlsPeer = new TlsClientImpl(this);
    } else {
      dtlsProtocolObj = new DTLSServerProtocol(secureRandom);
      tlsPeer = new TlsServerImpl(this);
    }
    tlsPeerHasRaisedCloseNotifyWarning = false;

    final DatagramTransportImpl datagramTransport = new DatagramTransportImpl(componentID);

    datagramTransport.setConnector(connector);

    Thread connectThread =
        new Thread() {
          @Override
          public void run() {
            try {
              runInConnectThread(dtlsProtocolObj, tlsPeer, datagramTransport);
            } finally {
              if (Thread.currentThread().equals(DtlsPacketTransformer.this.connectThread)) {
                DtlsPacketTransformer.this.connectThread = null;
              }
            }
          }
        };

    connectThread.setDaemon(true);
    connectThread.setName(DtlsPacketTransformer.class.getName() + ".connectThread");

    this.connectThread = connectThread;
    this.datagramTransport = datagramTransport;

    boolean started = false;

    try {
      connectThread.start();
      started = true;
    } finally {
      if (!started) {
        if (connectThread.equals(this.connectThread)) this.connectThread = null;
        if (datagramTransport.equals(this.datagramTransport)) this.datagramTransport = null;
      }
    }

    notifyAll();
  }
Exemplo n.º 3
0
  /**
   * Handles control packet.
   *
   * @param data raw packet data that arrived on control PPID.
   * @param sid SCTP stream id on which the data has arrived.
   */
  private synchronized void onCtrlPacket(byte[] data, int sid) throws IOException {
    ByteBuffer buffer = ByteBuffer.wrap(data);
    int messageType = /* 1 byte unsigned integer */ 0xFF & buffer.get();

    if (messageType == MSG_CHANNEL_ACK) {
      if (logger.isDebugEnabled()) {
        logger.debug(getEndpoint().getID() + " ACK received SID: " + sid);
      }
      // Open channel ACK
      WebRtcDataStream channel = channels.get(sid);
      if (channel != null) {
        // Ack check prevents from firing multiple notifications
        // if we get more than one ACKs (by mistake/bug).
        if (!channel.isAcknowledged()) {
          channel.ackReceived();
          notifyChannelOpened(channel);
        } else {
          logger.warn("Redundant ACK received for SID: " + sid);
        }
      } else {
        logger.error("No channel exists on sid: " + sid);
      }
    } else if (messageType == MSG_OPEN_CHANNEL) {
      int channelType = /* 1 byte unsigned integer */ 0xFF & buffer.get();
      int priority = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort();
      long reliability = /* 4 bytes unsigned integer */ 0xFFFFFFFFL & buffer.getInt();
      int labelLength = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort();
      int protocolLength = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort();
      String label;
      String protocol;

      if (labelLength == 0) {
        label = "";
      } else {
        byte[] labelBytes = new byte[labelLength];

        buffer.get(labelBytes);
        label = new String(labelBytes, "UTF-8");
      }
      if (protocolLength == 0) {
        protocol = "";
      } else {
        byte[] protocolBytes = new byte[protocolLength];

        buffer.get(protocolBytes);
        protocol = new String(protocolBytes, "UTF-8");
      }

      if (logger.isDebugEnabled()) {
        logger.debug(
            "!!! "
                + getEndpoint().getID()
                + " data channel open request on SID: "
                + sid
                + " type: "
                + channelType
                + " prio: "
                + priority
                + " reliab: "
                + reliability
                + " label: "
                + label
                + " proto: "
                + protocol);
      }

      if (channels.containsKey(sid)) {
        logger.error("Channel on sid: " + sid + " already exists");
      }

      WebRtcDataStream newChannel = new WebRtcDataStream(sctpSocket, sid, label, true);
      channels.put(sid, newChannel);

      sendOpenChannelAck(sid);

      notifyChannelOpened(newChannel);
    } else {
      logger.error("Unexpected ctrl msg type: " + messageType);
    }
  }
Exemplo n.º 4
0
  /** {@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;
  }