/** * Handles a specific STUN error <tt>Response</tt> with error code "438 Stale Nonce" to a specific * STUN <tt>Request</tt>. * * @param response the received STUN error <tt>Response</tt> with error code "438 Stale Nonce" * which is to be handled * @param request the STUN <tt>Request</tt> to which <tt>response</tt> responds * @param transactionID the <tt>TransactionID</tt> of <tt>response</tt> and <tt>request</tt> * because <tt>response</tt> and <tt>request</tt> only have 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 specified STUN error <tt>response</tt> was successfully handled; * <tt>false</tt>, otherwise * @throws StunException if anything goes wrong while handling the specified "438 Stale Nonce" * error <tt>response</tt> */ private boolean processStaleNonce(Response response, Request request, TransactionID transactionID) throws StunException { /* * The request MUST contain USERNAME, REALM, NONCE and MESSAGE-INTEGRITY * attributes. */ boolean challenge; if (request.getAttributeCount() > 0) { char[] includedRequestAttributeTypes = new char[] { Attribute.USERNAME, Attribute.REALM, Attribute.NONCE, Attribute.MESSAGE_INTEGRITY }; challenge = true; for (char includedRequestAttributeType : includedRequestAttributeTypes) { if (!request.containsAttribute(includedRequestAttributeType)) { challenge = false; break; } } } else challenge = false; return (challenge && processChallenge(response, request, transactionID)); }
/** * Handles a specific STUN error <tt>Response</tt> with error code "401 Unauthorized" to a * specific STUN <tt>Request</tt>. * * @param response the received STUN error <tt>Response</tt> with error code "401 Unauthorized" * which is to be handled * @param request the STUN <tt>Request</tt> to which <tt>response</tt> responds * @param transactionID the <tt>TransactionID</tt> of <tt>response</tt> and <tt>request</tt> * because <tt>response</tt> and <tt>request</tt> only have 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 specified STUN error <tt>response</tt> was successfully handled; * <tt>false</tt>, otherwise * @throws StunException if anything goes wrong while handling the specified "401 Unauthorized" * error <tt>response</tt> */ private boolean processUnauthorized( Response response, Request request, TransactionID transactionID) throws StunException { /* * If the response is a challenge, retry the request with a new * transaction. */ boolean challenge = true; /* * The client SHOULD omit the USERNAME, MESSAGE-INTEGRITY, REALM, and * NONCE attributes from the "First Request". */ if (request.getAttributeCount() > 0) { char[] excludedRequestAttributeTypes = new char[] { Attribute.USERNAME, Attribute.MESSAGE_INTEGRITY, Attribute.REALM, Attribute.NONCE }; for (char excludedRequestAttributeType : excludedRequestAttributeTypes) { if (request.containsAttribute(excludedRequestAttributeType)) { challenge = false; break; } } } return (challenge && processChallenge(response, request, transactionID)); }
/** * 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); } }