/**
   * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners)
   * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the
   * request URI, and then at the To field to find a matching candidate for dispatching. Note that
   * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because
   * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>.
   *
   * @param request the <tt>Request</tt> to find a recipient for.
   * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>.
   */
  private ProtocolProviderServiceSipImpl findTargetFor(Request request) {
    if (request == null) {
      logger.error("request shouldn't be null.");
      return null;
    }

    List<ProtocolProviderServiceSipImpl> currentListenersCopy =
        new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners());

    // Let's first narrow down candidate choice by comparing
    // addresses and ports (no point in delivering to a provider with a
    // non matching IP address  since they will reject it anyway).
    filterByAddress(currentListenersCopy, request);

    if (currentListenersCopy.size() == 0) {
      logger.error("no listeners");
      return null;
    }

    URI requestURI = request.getRequestURI();

    if (requestURI.isSipURI()) {
      String requestUser = ((SipURI) requestURI).getUser();

      List<ProtocolProviderServiceSipImpl> candidates =
          new ArrayList<ProtocolProviderServiceSipImpl>();

      // check if the Request-URI username is
      // one of ours usernames
      for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) {
        String ourUserID = listener.getAccountID().getUserID();
        // logger.trace(ourUserID + " *** " + requestUser);
        if (ourUserID.equals(requestUser)) {
          if (logger.isTraceEnabled())
            logger.trace("suitable candidate found: " + listener.getAccountID());
          candidates.add(listener);
        }
      }

      // the perfect match
      // every other case is approximation
      if (candidates.size() == 1) {
        ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0);

        if (logger.isTraceEnabled())
          logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\"");
        return perfectMatch;
      }

      // more than one account match
      if (candidates.size() > 1) {
        // check if a custom param exists in the contact
        // address (set for registrar accounts)
        for (ProtocolProviderServiceSipImpl candidate : candidates) {
          String hostValue =
              ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME);
          if (hostValue == null) continue;
          if (hostValue.equals(candidate.getContactAddressCustomParamValue())) {
            if (logger.isTraceEnabled())
              logger.trace(
                  "Will dispatch to \""
                      + candidate.getAccountID()
                      + "\" because "
                      + "\" the custom param was set");
            return candidate;
          }
        }

        // Past this point, our guess is not reliable. We try to find
        // the "least worst" match based on parameters like the To field

        // check if the To header field host part
        // matches any of our SIP hosts
        for (ProtocolProviderServiceSipImpl candidate : candidates) {
          URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI();
          if (fromURI.isSipURI() == false) continue;
          SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI();
          String ourHost = ourURI.getHost();

          URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI();
          if (toURI.isSipURI() == false) continue;
          String toHost = ((SipURI) toURI).getHost();

          // logger.trace(toHost + "***" + ourHost);
          if (toHost.equals(ourHost)) {
            if (logger.isTraceEnabled())
              logger.trace(
                  "Will dispatch to \""
                      + candidate.getAccountID()
                      + "\" because "
                      + "host in the To: is the same as in our AOR");
            return candidate;
          }
        }

        // fallback on the first candidate
        ProtocolProviderServiceSipImpl target = candidates.iterator().next();
        logger.info(
            "Will randomly dispatch to \""
                + target.getAccountID()
                + "\" because there is ambiguity on the username from"
                + " the Request-URI");
        if (logger.isTraceEnabled()) logger.trace("\n" + request);
        return target;
      }

      // fallback on any account
      ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next();
      if (logger.isDebugEnabled())
        logger.debug(
            "Will randomly dispatch to \""
                + target.getAccountID()
                + "\" because the username in the Request-URI "
                + "is unknown or empty");
      if (logger.isTraceEnabled()) logger.trace("\n" + request);
      return target;
    } else {
      logger.error("Request-URI is not a SIP URI, dropping");
    }
    return null;
  }
Exemple #2
0
  /**
   * Return addresses for default proxy to forward the request to. The list is organized in the
   * following priority. If the requestURI refers directly to a host, the host and port information
   * are extracted from it and made the next hop on the list. If the default route has been
   * specified, then it is used to construct the next element of the list. <code>
   * RouteHeader firstRoute = (RouteHeader) req.getHeader( RouteHeader.NAME );
   * if (firstRoute!=null) {
   *   URI uri = firstRoute.getAddress().getURI();
   *    if (uri.isSIPUri()) {
   *       SipURI nextHop = (SipURI) uri;
   *       if ( nextHop.hasLrParam() ) {
   *           // OK, use it
   *       } else {
   *           nextHop = fixStrictRouting( req );        <--- Here, make the modifications as per RFC3261
   *       }
   *   } else {
   *       // error: non-SIP URI not allowed in Route headers
   *       throw new SipException( "Request has Route header with non-SIP URI" );
   *   }
   * } else if (outboundProxy!=null) {
   *   // use outbound proxy for nextHop
   * } else if ( req.getRequestURI().isSipURI() ) {
   *   // use request URI for nextHop
   * }
   *
   * </code>
   *
   * @param request is the sip request to route.
   */
  public Hop getNextHop(Request request) throws SipException {

    SIPRequest sipRequest = (SIPRequest) request;

    RequestLine requestLine = sipRequest.getRequestLine();
    if (requestLine == null) {
      return defaultRoute;
    }
    javax.sip.address.URI requestURI = requestLine.getUri();
    if (requestURI == null) throw new IllegalArgumentException("Bad message: Null requestURI");

    RouteList routes = sipRequest.getRouteHeaders();

    /*
     * In case the topmost Route header contains no 'lr' parameter (which
     * means the next hop is a strict router), the implementation will
     * perform 'Route Information Postprocessing' as described in RFC3261
     * section 16.6 step 6 (also known as "Route header popping"). That is,
     * the following modifications will be made to the request:
     *
     * The implementation places the Request-URI into the Route header field
     * as the last value.
     *
     * The implementation then places the first Route header field value
     * into the Request-URI and removes that value from the Route header
     * field.
     *
     * Subsequently, the request URI will be used as next hop target
     */

    if (routes != null) {

      // to send the request through a specified hop the application is
      // supposed to prepend the appropriate Route header which.
      Route route = (Route) routes.getFirst();
      URI uri = route.getAddress().getURI();
      if (uri.isSipURI()) {
        SipURI sipUri = (SipURI) uri;
        if (!sipUri.hasLrParam()) {

          fixStrictRouting(sipRequest);
          if (sipStack.isLoggingEnabled())
            sipStack.getStackLogger().logDebug("Route post processing fixed strict routing");
        }

        Hop hop = createHop(sipUri, request);
        if (sipStack.isLoggingEnabled())
          sipStack.getStackLogger().logDebug("NextHop based on Route:" + hop);
        return hop;
      } else {
        throw new SipException("First Route not a SIP URI");
      }

    } else if (requestURI.isSipURI() && ((SipURI) requestURI).getMAddrParam() != null) {
      Hop hop = createHop((SipURI) requestURI, request);
      if (sipStack.isLoggingEnabled())
        sipStack
            .getStackLogger()
            .logDebug("Using request URI maddr to route the request = " + hop.toString());

      // JvB: don't remove it!
      // ((SipURI) requestURI).removeParameter("maddr");

      return hop;

    } else if (defaultRoute != null) {
      if (sipStack.isLoggingEnabled())
        sipStack
            .getStackLogger()
            .logDebug("Using outbound proxy to route the request = " + defaultRoute.toString());
      return defaultRoute;
    } else if (requestURI.isSipURI()) {
      Hop hop = createHop((SipURI) requestURI, request);
      if (hop != null && sipStack.isLoggingEnabled())
        sipStack.getStackLogger().logDebug("Used request-URI for nextHop = " + hop.toString());
      else if (sipStack.isLoggingEnabled()) {
        sipStack.getStackLogger().logDebug("returning null hop -- loop detected");
      }
      return hop;

    } else {
      // The internal router should never be consulted for non-sip URIs.
      InternalErrorHandler.handleException(
          "Unexpected non-sip URI", this.sipStack.getStackLogger());
      return null;
    }
  }