content: /*
   * Conditions Of Use
   *
   * This software was developed by employees of the National Institute of
   * Standards and Technology (NIST), an agency of the Federal Government.
   * Pursuant to title 15 Untied States Code Section 105, works of NIST
   * employees are not subject to copyright protection in the United States
   * and are considered to be in the public domain.  As a result, a formal
   * license is not needed to use the software.
   *
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   *
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *
   * .
   *
   */
  /*******************************************************************************
   * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).       *
   ******************************************************************************/
  package gov2.nist.javax2.sip.stack;
  
  import gov2.nist.core.*;
  import gov2.nist.core.net.AddressResolver;
  import gov2.nist.javax2.sip.*;
  import gov2.nist.javax2.sip.address.*;
  import gov2.nist.javax2.sip.header.*;
  import gov2.nist.javax2.sip.message.*;
  
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.ListIterator;
  
  import javax2.sip.*;
  import javax2.sip.address.*;
  import javax2.sip.header.RouteHeader;
  import javax2.sip.header.ViaHeader;
  import javax2.sip.message.*;
  
  /*
   * Bug reported by Will Scullin -- maddr was being ignored when routing
   * requests. Bug reported by Antonis Karydas - the RequestURI can be a non-sip
   * URI Jiang He - use address in route header. Significant changes to conform to
   * RFC 3261 made by Jeroen van Bemmel. Hagai Sela contributed a bug fix to the
   * strict route post processing code.
   *
   */
  
  /**
   * This is the default router. When the implementation wants to forward a
   * request and had run out of othe options, then it calls this method to figure
   * out where to send the request. The default router implements a simple
   * "default routing algorithm" which just forwards to the configured proxy
   * address.
   *
   * <p>
   * When <code>javax2.sip.USE_ROUTER_FOR_ALL_URIS</code> is set to
   * <code>false</code>, the next hop is determined according to the following
   * algorithm:
   * <ul>
   * <li> If the request contains one or more Route headers, use the URI of the
   * topmost Route header as next hop, possibly modifying the request in the
   * process if the topmost Route header contains no lr parameter(*)
   * <li> Else, if the property <code>javax2.sip.OUTBOUND_PROXY</code> is set,
   * use its value as the next hop
   * <li> Otherwise, use the request URI as next hop. If the request URI is not a
   * SIP URI, call {@link javax2.sip.address.Router#getNextHop(Request)} provided
   * by the application.
   * </ul>
   *
   * <p>
   * (*)Note that 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:
   * <ol>
   * <li>The implementation places the Request-URI into the Route header field as
   * the last value.
   * <li>The implementation then places the first Route header field value into
   * the Request-URI and removes that value from the Route header field.
   * </ol>
   * Subsequently, the request URI will be used as next hop target
   *
   *
   * @version 1.2 $Revision: 1.17 $ $Date: 2009/11/14 20:06:17 $
   *
   * @author M. Ranganathan <br/>
   *
   */
  public class DefaultRouter implements Router {
  
      private SipStackImpl sipStack;
  
      private Hop defaultRoute;
  
      private DefaultRouter() {
  
      }
  
      /**
       * Constructor.
       */
      public DefaultRouter(SipStack sipStack, String defaultRoute) {
          this.sipStack = (SipStackImpl) sipStack;
          if (defaultRoute != null) {
              try {
                  this.defaultRoute = (Hop) this.sipStack.getAddressResolver()
                          .resolveAddress((Hop) (new HopImpl(defaultRoute)));
              } catch (IllegalArgumentException ex) {
                  // The outbound proxy is optional. If specified it should be host:port/transport.
                  ((SIPTransactionStack) sipStack)
                          .getStackLogger()
                          .logError(
                                  "Invalid default route specification - need host:port/transport");
                  throw ex;
              }
          }
      }
  
      /**
       * 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;
          }
          javax2.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;
          }
  
      }
  
      /**
       * Performs strict router fix according to RFC3261 section 16.6 step 6
       *
       * pre: top route header in request has no 'lr' parameter in URI post:
       * request-URI added as last route header, new req-URI = top-route-URI
       */
      public void fixStrictRouting(SIPRequest req) {
  
          RouteList routes = req.getRouteHeaders();
          Route first = (Route) routes.getFirst();
          SipUri firstUri = (SipUri) first.getAddress().getURI();
          routes.removeFirst();
  
          // Add request-URI as last Route entry
          AddressImpl addr = new AddressImpl();
          addr.setAddess(req.getRequestURI()); // don't clone it
          Route route = new Route(addr);
  
          routes.add(route); // as last one
          req.setRequestURI(firstUri);
          if (sipStack.isLoggingEnabled()) {
              sipStack.getStackLogger().logDebug("post: fixStrictRouting" + req);
          }
      }
  
      /**
       * Utility method to create a hop from a SIP URI
       *
       * @param sipUri
       * @return
       */
  
  
      private final Hop createHop(SipURI sipUri, Request request) {
          // always use TLS when secure
          String transport = sipUri.isSecure() ? SIPConstants.TLS : sipUri
                  .getTransportParam();
          if (transport == null) {
              //@see issue 131
              ViaHeader via = (ViaHeader) request.getHeader(ViaHeader.NAME);
              transport = via.getTransport();
          }
  
          // sipUri.removeParameter("transport");
  
          int port;
          if (sipUri.getPort() != -1) {
              port = sipUri.getPort();
          } else {
              if (transport.equalsIgnoreCase(SIPConstants.TLS))
                  port = 5061;
              else
                  port = 5060; // TCP or UDP
          }
          String host = sipUri.getMAddrParam() != null ? sipUri.getMAddrParam()
                  : sipUri.getHost();
          AddressResolver addressResolver = this.sipStack.getAddressResolver();
          return addressResolver
                  .resolveAddress(new HopImpl(host, port, transport));
  
      }
  
      /**
       * Get the default hop.
       *
       * @return defaultRoute is the default route. public java.util.Iterator
       *         getDefaultRoute(Request request) { return
       *         this.getNextHops((SIPRequest)request); }
       */
  
      public javax2.sip.address.Hop getOutboundProxy() {
          return this.defaultRoute;
      }
  
      /*
       * (non-Javadoc)
       *
       * @see javax2.sip.address.Router#getNextHop(javax2.sip.message.Request)
       */
      public ListIterator getNextHops(Request request) {
          try {
              LinkedList llist = new LinkedList();
              llist.add(this.getNextHop(request));
              return llist.listIterator();
          } catch (SipException ex) {
              return null;
          }
  
      }
  }
  