/** * 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); } } } } } } }