/**
   * 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));
  }
  /**
   * 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));
  }
  /**
   * Adds the <tt>Attribute</tt>s to a specific <tt>Request</tt> which support the STUN short-term
   * credential mechanism if the mechanism in question is utilized by this
   * <tt>StunCandidateHarvest</tt> (i.e. by the associated <tt>StunCandidateHarvester</tt>).
   *
   * @param request the <tt>Request</tt> to which to add the <tt>Attribute</tt>s supporting the STUN
   *     short-term credential mechanism if the mechanism in question is utilized by this
   *     <tt>StunCandidateHarvest</tt>
   * @return <tt>true</tt> if the STUN short-term credential mechanism is actually utilized by this
   *     <tt>StunCandidateHarvest</tt> for the specified <tt>request</tt>; otherwise, <tt>false</tt>
   */
  protected boolean addShortTermCredentialAttributes(Request request) {
    String shortTermCredentialUsername = harvester.getShortTermCredentialUsername();

    if (shortTermCredentialUsername != null) {
      request.putAttribute(AttributeFactory.createUsernameAttribute(shortTermCredentialUsername));
      request.putAttribute(
          AttributeFactory.createMessageIntegrityAttribute(shortTermCredentialUsername));
      return true;
    } else return false;
  }
 /**
  * Creates a new <tt>Request</tt> instance which is to be sent by this
  * <tt>StunCandidateHarvest</tt> in order to retry a specific <tt>Request</tt>. For example, the
  * long-term credential mechanism dictates that a <tt>Request</tt> is first sent by the client
  * without any credential-related attributes, then it gets challenged by the server and the client
  * retries the original <tt>Request</tt> with the appropriate credential-related attributes in
  * response.
  *
  * @param request the <tt>Request</tt> which is to be retried by this
  *     <tt>StunCandidateHarvest</tt>
  * @return the new <tt>Request</tt> instance which is to be sent by this
  *     <tt>StunCandidateHarvest</tt> in order to retry the specified <tt>request</tt>
  */
 protected Request createRequestToRetry(Request request) {
   switch (request.getMessageType()) {
     case Message.BINDING_REQUEST:
       return MessageFactory.createBindingRequest();
     default:
       throw new IllegalArgumentException("request.messageType");
   }
 }
Beispiel #5
0
 public void sendreqs() {
   long now = System.currentTimeMillis();
   synchronized (req) {
     for (Iterator<Map.Entry<Coord, Request>> i = req.entrySet().iterator(); i.hasNext(); ) {
       Map.Entry<Coord, Request> e = i.next();
       Coord c = e.getKey();
       Request r = e.getValue();
       if (now - r.lastreq > 1000) {
         r.lastreq = now;
         if (++r.reqs >= 5) {
           i.remove();
         } else {
           Message msg = new Message(Session.MSG_MAPREQ);
           msg.addcoord(c);
           sess.sendmsg(msg);
         }
       }
     }
   }
 }
  /**
   * 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;
  }
    protected String resolveURL(ApiWrapper wrapper, String url) throws IOException {
      String result = "";

      HttpResponse reso = wrapper.head(Request.to(Endpoints.RESOLVE).with("url", url));
      if (reso.getStatusLine().getStatusCode() != HttpStatus.SC_MOVED_TEMPORARILY)
        throw new CloudAPI.ResolverException("Invalid status code", reso);

      // Header location = reso.getFirstHeader("Location");
      if (!reso.containsHeader("Location"))
        throw new CloudAPI.ResolverException("No location header", reso);
      result = reso.getFirstHeader("Location").getValue();

      return result;
    }
  /**
   * 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);
  }
    @Override
    protected String[] doInBackground(String... parms) {
      String[] resultarr = new String[1];
      String intentstr = parms[0];

      // int count = urls.length;
      // long totalSize = 0; for (int i = 0; i < count; i++) { totalSize +=
      // Downloader.downloadFile(urls[i]);
      // publishProgress((int) ((i / (float) count) * 100));
      // Escape early if cancel() is called
      // if (isCancelled())
      //		break;

      try {
        // Connect to API and authenticate
        publishProgress("Connecting and authenticating API session...");

        ApiWrapper wrapper;
        // wrapper = Api.wrapper;
        wrapper = new ApiWrapper(Api.mClientID, Api.mClientSecret, null, null);
        wrapper.login("un1tz3r0", "Farscap3");

        publishProgress("Resolving url...");

        String resolvedurl = resolveURL(wrapper, intentstr);

        publishProgress("Getting metadata...");

        HttpResponse resp = wrapper.get(Request.to(resolvedurl));

        JSONObject jso = Http.getJSON(resp);
        // resultstr = jso.toString();

        if (jso.getString("kind").equals("track")) {
          if (jso.getBoolean("downloadable")) {
            publishProgress("Getting download redirect URL...");

            String dlrurl =
                wrapper
                    .getURI(
                        Request.to(jso.getString("download_url")).add("allow_redirect", false),
                        false,
                        false)
                    .toString();
            HttpResponse dlrresp = wrapper.get(Request.to(dlrurl));
            String dlstr = dlrurl;
            if (dlrresp.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) {
              Header dlloch = dlrresp.getFirstHeader("location");
              if ((dlloch == null) || (dlloch.getValue() == null))
                throw new RuntimeException("Download url HEAD response has no location header");

              dlstr = wrapper.getURI(Request.to(dlloch.getValue()), false, false).toString();
            } else if (dlrresp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              dlstr = dlrurl;
            } else {
              throw new RuntimeException(
                  "Download url HEAD response has wrong status ("
                      + String.valueOf(dlrresp.getStatusLine().getStatusCode())
                      + " "
                      + dlrresp.getStatusLine().getReasonPhrase()
                      + ")");
            }
            // String dlstr = Request.to( dlloch.getValue() ).add("CLIENT_ID",
            // Api.mClientID).toString();

            //												if(dlresp2.getStatusLine().getStatusCode() !=
            // HttpStatus.SC_MOVED_TEMPORARILY)
            //														throw new RuntimeException("Download redirect url HEAD response has
            // wrong status: " + dlresp2.getStatusLine().toString());
            //												Header dlloc2 = dlresp2.getFirstHeader("location");
            //												if((dlloc2 == null) || (dlloc2.getValue() == null))
            //														throw new RuntimeException("Download redirect url HEAD response has no
            // location header");
            //

            resultarr = new String[2];
            resultarr[1] =
                jso.getString("title").replaceAll("[^A-Za-z0-9 -]*", "")
                    + "."
                    + jso.getString("original_format");
            resultarr[0] = dlstr;
          } else {
            Stream st = wrapper.resolveStreamUrl(jso.getString("stream_url"), true);
            resultarr = new String[2];
            resultarr[1] = jso.getString("title").replaceAll("[^A-Za-z0-9 -]*", "").concat(".mp3");
            resultarr[0] = st.streamUrl;
          }
        }
      } catch (JSONException e) {
        resultarr = new String[1];
        resultarr[0] = e.toString();
      } catch (IOException e) {
        resultarr = new String[1];
        resultarr[0] = e.toString();
      } catch (Exception e) {
        resultarr = new String[1];
        resultarr[0] = e.toString();
      }

      return resultarr;
    }