/** * Notifies this <tt>ResponseCollector</tt> that a STUN response described by the specified * <tt>StunResponseEvent</tt> has been received. * * @param event the <tt>StunResponseEvent</tt> which describes the received STUN response * @see ResponseCollector#processResponse(StunResponseEvent) */ @Override public void processResponse(StunResponseEvent event) { TransactionID transactionID = event.getTransactionID(); logger.finest("Received a message: tranid= " + transactionID); logger.finest("localCand= " + hostCandidate); /* * Clean up for the purposes of the workaround which determines the STUN * Request to which a STUN Response responds. */ synchronized (requests) { requests.remove(transactionID); } // At long last, do start handling the received STUN Response. Response response = event.getResponse(); Request request = event.getRequest(); boolean completedResolvingCandidate = true; try { if (response.isSuccessResponse()) { // Authentication and Message-Integrity Mechanisms if (request.containsAttribute(Attribute.MESSAGE_INTEGRITY)) { MessageIntegrityAttribute messageIntegrityAttribute = (MessageIntegrityAttribute) response.getAttribute(Attribute.MESSAGE_INTEGRITY); /* * RFC 5389: If MESSAGE-INTEGRITY was absent, the response * MUST be discarded, as if it was never received. */ if (messageIntegrityAttribute == null) return; UsernameAttribute usernameAttribute = (UsernameAttribute) request.getAttribute(Attribute.USERNAME); /* * For a request or indication message, the agent MUST * include the USERNAME and MESSAGE-INTEGRITY attributes in * the message. */ if (usernameAttribute == null) return; if (!harvester .getStunStack() .validateMessageIntegrity( messageIntegrityAttribute, LongTermCredential.toString(usernameAttribute.getUsername()), !request.containsAttribute(Attribute.REALM) && !request.containsAttribute(Attribute.NONCE), event.getRawMessage())) return; } processSuccess(response, request, transactionID); } else { ErrorCodeAttribute errorCodeAttr = (ErrorCodeAttribute) response.getAttribute(Attribute.ERROR_CODE); if ((errorCodeAttr != null) && (errorCodeAttr.getErrorClass() == 4)) { try { switch (errorCodeAttr.getErrorNumber()) { case 1: // 401 Unauthorized if (processUnauthorized(response, request, transactionID)) completedResolvingCandidate = false; break; case 38: // 438 Stale Nonce if (processStaleNonce(response, request, transactionID)) completedResolvingCandidate = false; break; } } catch (StunException sex) { completedResolvingCandidate = true; } } if (completedResolvingCandidate && processErrorOrFailure(response, request, transactionID)) completedResolvingCandidate = false; } } finally { if (completedResolvingCandidate) completedResolvingCandidate(request, response); } }
/** * Notifies this <tt>StunCandidateHarvest</tt> that a specific STUN <tt>Request</tt> has been * challenged for a long-term credential (as the short-term credential mechanism does not utilize * challenging) in a specific <tt>realm</tt> and with a specific <tt>nonce</tt>. * * @param realm the realm in which the specified STUN <tt>Request</tt> has been challenged for a * long-term credential * @param nonce the nonce with which the specified STUN <tt>Request</tt> has been challenged for a * long-term credential * @param request the STUN <tt>Request</tt> which has been challenged for a long-term credential * @param requestTransactionID 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 * @return <tt>true</tt> if the challenge has been processed and this * <tt>StunCandidateHarvest</tt> is to continue processing STUN <tt>Response</tt>s; otherwise, * <tt>false</tt> * @throws StunException if anything goes wrong while processing the challenge */ private boolean processChallenge( byte[] realm, byte[] nonce, Request request, TransactionID requestTransactionID) throws StunException { UsernameAttribute usernameAttribute = (UsernameAttribute) request.getAttribute(Attribute.USERNAME); if (usernameAttribute == null) { if (longTermCredentialSession == null) { LongTermCredential longTermCredential = harvester.createLongTermCredential(this, realm); if (longTermCredential == null) { // The long-term credential mechanism is not being utilized. return false; } else { longTermCredentialSession = new LongTermCredentialSession(longTermCredential, realm); harvester .getStunStack() .getCredentialsManager() .registerAuthority(longTermCredentialSession); } } else { /* * If we're going to use the long-term credential to retry the * request, the long-term credential should be for the request * in terms of realm. */ if (!longTermCredentialSession.realmEquals(realm)) return false; } } else { /* * If we sent a USERNAME in our request, then we had the long-term * credential at the time we sent the request in question. */ if (longTermCredentialSession == null) return false; else { /* * If we're going to use the long-term credential to retry the * request, the long-term credential should be for the request * in terms of username. */ if (!longTermCredentialSession.usernameEquals(usernameAttribute.getUsername())) return false; else { // And it terms of realm, of course. if (!longTermCredentialSession.realmEquals(realm)) return false; } } } /* * The nonce is either becoming known for the first time or being * updated after the old one has gone stale. */ longTermCredentialSession.setNonce(nonce); Request retryRequest = createRequestToRetry(request); TransactionID retryRequestTransactionID = null; if (retryRequest != null) { if (requestTransactionID != null) { Object applicationData = requestTransactionID.getApplicationData(); if (applicationData != null) { byte[] retryRequestTransactionIDAsBytes = retryRequest.getTransactionID(); retryRequestTransactionID = (retryRequestTransactionIDAsBytes == null) ? TransactionID.createNewTransactionID() : TransactionID.createTransactionID( harvester.getStunStack(), retryRequestTransactionIDAsBytes); retryRequestTransactionID.setApplicationData(applicationData); } } retryRequestTransactionID = sendRequest(retryRequest, false, retryRequestTransactionID); } return (retryRequestTransactionID != null); }