private void handleAllocationRequest() throws StunException, IOException, InterruptedException {
    StunMessageEvent event = eventsReceivedByTurnServer.poll(5, TimeUnit.SECONDS);
    TransportAddress localAddress = event.getLocalAddress();
    TransportAddress remoteAddress = event.getRemoteAddress();

    Request request = (Request) event.getMessage();
    assertThat(request.getMessageType(), is(Message.ALLOCATE_REQUEST));
    Response allocationResponse =
        MessageFactory.createAllocationResponse(
            request,
            remoteAddress,
            new TransportAddress(localAddress.getHostAddress(), 2222, UDP),
            100);

    sendResponse(remoteAddress, request, allocationResponse);
  }
  /**
   * Returns the result of applying XOR on the specified attribute's address. The method may be used
   * for both encoding and decoding XorMappedAddresses.
   *
   * @param address the address on which XOR should be applied
   * @param transactionID the transaction id to use for the XOR
   * @return the XOR-ed address.
   */
  public static TransportAddress applyXor(TransportAddress address, byte[] transactionID) {
    byte[] addressBytes = address.getAddressBytes();
    char port = (char) address.getPort();

    char portModifier =
        (char) ((transactionID[0] << 8 & 0x0000FF00) | (transactionID[1] & 0x000000FF));

    port ^= portModifier;

    for (int i = 0; i < addressBytes.length; i++) addressBytes[i] ^= transactionID[i];

    TransportAddress xoredAdd;
    try {
      xoredAdd = new TransportAddress(addressBytes, port, Transport.UDP);
    } catch (UnknownHostException e) {
      // shouldn't happen so just throw an illegal arg
      throw new IllegalArgumentException(e);
    }

    return xoredAdd;
  }
  /**
   * Determines whether a specific {@code DatagramPacket} is accepted by {@link #channelDataSocket}
   * (i.e. whether {@code channelDataSocket} understands {@code packet} and {@code packet} is meant
   * to be received by {@code channelDataSocket}).
   *
   * @param packet the {@code DatagramPacket} which is to be checked whether it is accepted by
   *     {@code channelDataSocket}
   * @return {@code true} if {@code channelDataSocket} accepts {@code packet} (i.e. {@code
   *     channelDataSocket} understands {@code packet} and {@code p} is meant to be received by
   *     {@code channelDataSocket}); otherwise, {@code false}
   */
  private boolean isChannelData(DatagramPacket packet) {
    // Is it from our TURN server?
    if (!serverAddress.equals(packet.getSocketAddress())) {
      return false;
    }

    int packetLength = packet.getLength();

    if (packetLength < (CHANNELDATA_CHANNELNUMBER_LENGTH + CHANNELDATA_LENGTH_LENGTH)) {
      return false;
    }

    byte[] pData = packet.getData();
    int pOffset = packet.getOffset();

    /*
     * The first two bits should be 0b01 because of the current channel number range 0x4000 - 0x7FFE. But 0b10 and 0b11
     * which are currently reserved and may be used in the future to extend the range of channel numbers.
     */
    if ((pData[pOffset] & 0xC0) == 0) {
      return false;
    }

    pOffset += CHANNELDATA_CHANNELNUMBER_LENGTH;
    packetLength -= CHANNELDATA_CHANNELNUMBER_LENGTH;

    int length = ((pData[pOffset++] << 8) | (pData[pOffset] & 0xFF));

    int padding = ((length % 4) > 0) ? 4 - (length % 4) : 0;

    /*
     * The Length field specifies the length in bytes of the Application Data field. The Length field does not include
     * the padding that is sometimes present in the data of the DatagramPacket.
     */
    return length == packetLength - padding - CHANNELDATA_LENGTH_LENGTH
        || length == packetLength - CHANNELDATA_LENGTH_LENGTH;
  }
Beispiel #4
0
  /** The listening thread's run method. */
  @Override
  public void run() {
    DatagramPacket packet = null;

    while (this.running) {
      try {
        IceSocketWrapper localSock;

        synchronized (sockLock) {
          if (!running) return;

          localSock = this.sock;
        }

        /*
         * Make sure localSock's receiveBufferSize is taken into
         * account including after it gets changed.
         */
        int receiveBufferSize = 1500;
        /*
        if(localSock.getTCPSocket() != null)
        {
            receiveBufferSize = localSock.getTCPSocket().
                getReceiveBufferSize();
        }
        else if(localSock.getUDPSocket() != null)
        {
            receiveBufferSize = localSock.getUDPSocket().
                getReceiveBufferSize();
        }
        */

        if (packet == null) {
          packet = new DatagramPacket(new byte[receiveBufferSize], receiveBufferSize);
        } else {
          byte[] packetData = packet.getData();

          if ((packetData == null) || (packetData.length < receiveBufferSize)) {
            packet.setData(new byte[receiveBufferSize], 0, receiveBufferSize);
          } else {
            /*
             * XXX Tell the packet it is large enough because the
             * socket will not look at the length of the data array
             * property and will just respect the length property.
             */
            packet.setLength(receiveBufferSize);
          }
        }

        localSock.receive(packet);

        // get lost if we are no longer running.
        if (!running) return;

        logger.finest("received datagram");

        RawMessage rawMessage =
            new RawMessage(
                packet.getData(),
                packet.getLength(),
                new TransportAddress(
                    packet.getAddress(), packet.getPort(), listenAddress.getTransport()),
                listenAddress);

        messageQueue.add(rawMessage);
      } catch (SocketException ex) {
        if (running) {
          logger.log(
              Level.WARNING, "Connector died: " + listenAddress + " -> " + remoteAddress, ex);

          stop();
          // Something wrong has happened
          errorHandler.handleFatalError(
              this, "A socket exception was thrown" + " while trying to receive a message.", ex);
        } else {
          // The exception was most probably caused by calling
          // this.stop().
        }
      } catch (ClosedChannelException cce) {
        logger.log(Level.WARNING, "A net access point has gone useless:", cce);

        stop();
        errorHandler.handleFatalError(
            this, "ClosedChannelException occurred while listening" + " for messages!", cce);
      } catch (IOException ex) {
        logger.log(Level.WARNING, "A net access point has gone useless:", ex);

        errorHandler.handleError(ex.getMessage(), ex);
        // do not stop the thread;
      } catch (Throwable ex) {
        logger.log(Level.WARNING, "A net access point has gone useless:", ex);

        stop();
        errorHandler.handleFatalError(
            this, "Unknown error occurred while listening for messages!", ex);
      }
    }
  }