/**
   * Sends a specific <tt>Request</tt> to the STUN server associated with this
   * <tt>StunCandidateHarvest</tt>.
   *
   * @param request the <tt>Request</tt> to send to the STUN server associated with this
   *     <tt>StunCandidateHarvest</tt>
   * @param firstRequest <tt>true</tt> if the specified <tt>request</tt> should be sent as the first
   *     request in the terms of STUN; otherwise, <tt>false</tt>
   * @return the <tt>TransactionID</tt> of the STUN client transaction through which the specified
   *     <tt>Request</tt> has been sent to the STUN server associated with this
   *     <tt>StunCandidateHarvest</tt>
   * @param transactionID the <tt>TransactionID</tt> of <tt>request</tt> because <tt>request</tt>
   *     only has it as a <tt>byte</tt> array and <tt>TransactionID</tt> is required for the
   *     <tt>applicationData</tt> property value
   * @throws StunException if anything goes wrong while sending the specified <tt>Request</tt> to
   *     the STUN server associated with this <tt>StunCandidateHarvest</tt>
   */
  protected TransactionID sendRequest(
      Request request, boolean firstRequest, TransactionID transactionID) throws StunException {
    if (!firstRequest && (longTermCredentialSession != null))
      longTermCredentialSession.addAttributes(request);

    StunStack stunStack = harvester.getStunStack();
    TransportAddress stunServer = harvester.stunServer;
    TransportAddress hostCandidateTransportAddress = hostCandidate.getTransportAddress();

    if (transactionID == null) {
      byte[] transactionIDAsBytes = request.getTransactionID();

      transactionID =
          (transactionIDAsBytes == null)
              ? TransactionID.createNewTransactionID()
              : TransactionID.createTransactionID(harvester.getStunStack(), transactionIDAsBytes);
    }
    synchronized (requests) {
      try {
        transactionID =
            stunStack.sendRequest(
                request, stunServer, hostCandidateTransportAddress, this, transactionID);
      } catch (IllegalArgumentException iaex) {
        if (logger.isLoggable(Level.INFO)) {
          logger.log(
              Level.INFO,
              "Failed to send "
                  + request
                  + " through "
                  + hostCandidateTransportAddress
                  + " to "
                  + stunServer,
              iaex);
        }
        throw new StunException(StunException.ILLEGAL_ARGUMENT, iaex.getMessage(), iaex);
      } catch (IOException ioex) {
        if (logger.isLoggable(Level.INFO)) {
          logger.log(
              Level.INFO,
              "Failed to send "
                  + request
                  + " through "
                  + hostCandidateTransportAddress
                  + " to "
                  + stunServer,
              ioex);
        }
        throw new StunException(StunException.NETWORK_ERROR, ioex.getMessage(), ioex);
      }

      requests.put(transactionID, request);
    }
    return transactionID;
  }
  /**
   * Creates a <tt>ServerReflexiveCandidate</tt> using {@link #hostCandidate} as its base and the
   * <tt>XOR-MAPPED-ADDRESS</tt> attribute in <tt>response</tt> for the actual
   * <tt>TransportAddress</tt> of the new candidate. If the message is malformed and/or does not
   * contain the corresponding attribute, this method simply has no effect.
   *
   * @param response the STUN <tt>Response</tt> which is supposed to contain the address we should
   *     use for the new candidate
   */
  protected void createServerReflexiveCandidate(Response response) {
    TransportAddress addr = getMappedAddress(response);

    if (addr != null) {
      ServerReflexiveCandidate srvrRflxCand = createServerReflexiveCandidate(addr);

      if (srvrRflxCand != null) {
        try {
          addCandidate(srvrRflxCand);
        } finally {
          // Free srvrRflxCand if it has not been consumed.
          if (!containsCandidate(srvrRflxCand)) {
            try {
              srvrRflxCand.free();
            } catch (Exception ex) {
              if (logger.isLoggable(Level.FINE)) {
                logger.log(
                    Level.FINE,
                    "Failed to free" + " ServerReflexiveCandidate: " + srvrRflxCand,
                    ex);
              }
            }
          }
        }
      }
    }
  }
  /**
   * Runs in {@link #sendKeepAliveMessageThread} and sends STUN keep-alive <tt>Message</tt>s to the
   * STUN server associated with the <tt>StunCandidateHarvester</tt> of this instance.
   *
   * @return <tt>true</tt> if the method is to be invoked again; otherwise, <tt>false</tt>
   */
  private boolean runInSendKeepAliveMessageThread() {
    synchronized (sendKeepAliveMessageSyncRoot) {
      // Since we're going to #wait, make sure we're not canceled yet.
      if (sendKeepAliveMessageThread != Thread.currentThread()) return false;
      if (sendKeepAliveMessageInterval == SEND_KEEP_ALIVE_MESSAGE_INTERVAL_NOT_SPECIFIED) {
        return false;
      }

      // Determine the amount of milliseconds that we'll have to #wait.
      long timeout;

      if (sendKeepAliveMessageTime == -1) {
        /*
         * If we're just starting, don't just go and send a new STUN
         * keep-alive message but rather wait for the whole interval.
         */
        timeout = sendKeepAliveMessageInterval;
      } else {
        timeout =
            sendKeepAliveMessageTime + sendKeepAliveMessageInterval - System.currentTimeMillis();
      }
      // At long last, #wait if necessary.
      if (timeout > 0) {
        try {
          sendKeepAliveMessageSyncRoot.wait(timeout);
        } catch (InterruptedException iex) {
        }
        /*
         * Apart from being the time to send the STUN keep-alive
         * message, it could be that we've experienced a spurious
         * wake-up or that we've been canceled.
         */
        return true;
      }
    }

    sendKeepAliveMessageTime = System.currentTimeMillis();
    try {
      sendKeepAliveMessage();
    } catch (StunException sex) {
      logger.log(Level.INFO, "Failed to send STUN keep-alive message.", sex);
    }
    return true;
  }
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);
      }
    }
  }