Home | History | Annotate | Download | only in stack
      1 /*
      2  * Conditions Of Use
      3  *
      4  * This software was developed by employees of the National Institute of
      5  * Standards and Technology (NIST), an agency of the Federal Government.
      6  * Pursuant to title 15 Untied States Code Section 105, works of NIST
      7  * employees are not subject to copyright protection in the United States
      8  * and are considered to be in the public domain.  As a result, a formal
      9  * license is not needed to use the software.
     10  *
     11  * This software is provided by NIST as a service and is expressly
     12  * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
     13  * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
     14  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
     15  * AND DATA ACCURACY.  NIST does not warrant or make any representations
     16  * regarding the use of the software or the results thereof, including but
     17  * not limited to the correctness, accuracy, reliability or usefulness of
     18  * the software.
     19  *
     20  * Permission to use this software is contingent upon your acceptance
     21  * of the terms of this agreement
     22  *
     23  * .
     24  *
     25  */
     26 /*******************************************************************************
     27  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).       *
     28  ******************************************************************************/
     29 package gov.nist.javax.sip.stack;
     30 
     31 import gov.nist.javax.sip.message.*;
     32 import gov.nist.javax.sip.address.*;
     33 import gov.nist.javax.sip.header.*;
     34 import gov.nist.javax.sip.*;
     35 import gov.nist.core.*;
     36 import gov.nist.core.net.AddressResolver;
     37 
     38 import javax.sip.*;
     39 import java.util.Iterator;
     40 import java.util.LinkedList;
     41 import java.util.ListIterator;
     42 
     43 import javax.sip.header.RouteHeader;
     44 import javax.sip.header.ViaHeader;
     45 import javax.sip.message.*;
     46 import javax.sip.address.*;
     47 
     48 /*
     49  * Bug reported by Will Scullin -- maddr was being ignored when routing
     50  * requests. Bug reported by Antonis Karydas - the RequestURI can be a non-sip
     51  * URI Jiang He - use address in route header. Significant changes to conform to
     52  * RFC 3261 made by Jeroen van Bemmel. Hagai Sela contributed a bug fix to the
     53  * strict route post processing code.
     54  *
     55  */
     56 
     57 /**
     58  * This is the default router. When the implementation wants to forward a
     59  * request and had run out of othe options, then it calls this method to figure
     60  * out where to send the request. The default router implements a simple
     61  * "default routing algorithm" which just forwards to the configured proxy
     62  * address.
     63  *
     64  * <p>
     65  * When <code>javax.sip.USE_ROUTER_FOR_ALL_URIS</code> is set to
     66  * <code>false</code>, the next hop is determined according to the following
     67  * algorithm:
     68  * <ul>
     69  * <li> If the request contains one or more Route headers, use the URI of the
     70  * topmost Route header as next hop, possibly modifying the request in the
     71  * process if the topmost Route header contains no lr parameter(*)
     72  * <li> Else, if the property <code>javax.sip.OUTBOUND_PROXY</code> is set,
     73  * use its value as the next hop
     74  * <li> Otherwise, use the request URI as next hop. If the request URI is not a
     75  * SIP URI, call {@link javax.sip.address.Router#getNextHop(Request)} provided
     76  * by the application.
     77  * </ul>
     78  *
     79  * <p>
     80  * (*)Note that in case the topmost Route header contains no 'lr' parameter
     81  * (which means the next hop is a strict router), the implementation will
     82  * perform 'Route Information Postprocessing' as described in RFC3261 section
     83  * 16.6 step 6 (also known as "Route header popping"). That is, the following
     84  * modifications will be made to the request:
     85  * <ol>
     86  * <li>The implementation places the Request-URI into the Route header field as
     87  * the last value.
     88  * <li>The implementation then places the first Route header field value into
     89  * the Request-URI and removes that value from the Route header field.
     90  * </ol>
     91  * Subsequently, the request URI will be used as next hop target
     92  *
     93  *
     94  * @version 1.2 $Revision: 1.17 $ $Date: 2009/11/14 20:06:17 $
     95  *
     96  * @author M. Ranganathan <br/>
     97  *
     98  */
     99 public class DefaultRouter implements Router {
    100 
    101     private SipStackImpl sipStack;
    102 
    103     private Hop defaultRoute;
    104 
    105     private DefaultRouter() {
    106 
    107     }
    108 
    109     /**
    110      * Constructor.
    111      */
    112     public DefaultRouter(SipStack sipStack, String defaultRoute) {
    113         this.sipStack = (SipStackImpl) sipStack;
    114         if (defaultRoute != null) {
    115             try {
    116                 this.defaultRoute = (Hop) this.sipStack.getAddressResolver()
    117                         .resolveAddress((Hop) (new HopImpl(defaultRoute)));
    118             } catch (IllegalArgumentException ex) {
    119                 // The outbound proxy is optional. If specified it should be host:port/transport.
    120                 ((SIPTransactionStack) sipStack)
    121                         .getStackLogger()
    122                         .logError(
    123                                 "Invalid default route specification - need host:port/transport");
    124                 throw ex;
    125             }
    126         }
    127     }
    128 
    129     /**
    130      * Return addresses for default proxy to forward the request to. The list is
    131      * organized in the following priority. If the requestURI refers directly to
    132      * a host, the host and port information are extracted from it and made the
    133      * next hop on the list. If the default route has been specified, then it is
    134      * used to construct the next element of the list. <code>
    135      * RouteHeader firstRoute = (RouteHeader) req.getHeader( RouteHeader.NAME );
    136      * if (firstRoute!=null) {
    137      *   URI uri = firstRoute.getAddress().getURI();
    138      *    if (uri.isSIPUri()) {
    139      *       SipURI nextHop = (SipURI) uri;
    140      *       if ( nextHop.hasLrParam() ) {
    141      *           // OK, use it
    142      *       } else {
    143      *           nextHop = fixStrictRouting( req );        <--- Here, make the modifications as per RFC3261
    144      *       }
    145      *   } else {
    146      *       // error: non-SIP URI not allowed in Route headers
    147      *       throw new SipException( "Request has Route header with non-SIP URI" );
    148      *   }
    149      * } else if (outboundProxy!=null) {
    150      *   // use outbound proxy for nextHop
    151      * } else if ( req.getRequestURI().isSipURI() ) {
    152      *   // use request URI for nextHop
    153      * }
    154      *
    155      * </code>
    156      *
    157      * @param request
    158      *            is the sip request to route.
    159      *
    160      */
    161     public Hop getNextHop(Request request) throws SipException {
    162 
    163         SIPRequest sipRequest = (SIPRequest) request;
    164 
    165         RequestLine requestLine = sipRequest.getRequestLine();
    166         if (requestLine == null) {
    167             return defaultRoute;
    168         }
    169         javax.sip.address.URI requestURI = requestLine.getUri();
    170         if (requestURI == null)
    171             throw new IllegalArgumentException("Bad message: Null requestURI");
    172 
    173         RouteList routes = sipRequest.getRouteHeaders();
    174 
    175         /*
    176          * In case the topmost Route header contains no 'lr' parameter (which
    177          * means the next hop is a strict router), the implementation will
    178          * perform 'Route Information Postprocessing' as described in RFC3261
    179          * section 16.6 step 6 (also known as "Route header popping"). That is,
    180          * the following modifications will be made to the request:
    181          *
    182          * The implementation places the Request-URI into the Route header field
    183          * as the last value.
    184          *
    185          * The implementation then places the first Route header field value
    186          * into the Request-URI and removes that value from the Route header
    187          * field.
    188          *
    189          * Subsequently, the request URI will be used as next hop target
    190          */
    191 
    192         if (routes != null) {
    193 
    194             // to send the request through a specified hop the application is
    195             // supposed to prepend the appropriate Route header which.
    196             Route route = (Route) routes.getFirst();
    197             URI uri = route.getAddress().getURI();
    198             if (uri.isSipURI()) {
    199                 SipURI sipUri = (SipURI) uri;
    200                 if (!sipUri.hasLrParam()) {
    201 
    202                     fixStrictRouting(sipRequest);
    203                     if (sipStack.isLoggingEnabled())
    204                         sipStack.getStackLogger()
    205                                 .logDebug("Route post processing fixed strict routing");
    206                 }
    207 
    208                 Hop hop = createHop(sipUri,request);
    209                 if (sipStack.isLoggingEnabled())
    210                     sipStack.getStackLogger()
    211                             .logDebug("NextHop based on Route:" + hop);
    212                 return hop;
    213             } else {
    214                 throw new SipException("First Route not a SIP URI");
    215             }
    216 
    217         } else if (requestURI.isSipURI()
    218                 && ((SipURI) requestURI).getMAddrParam() != null) {
    219             Hop hop = createHop((SipURI) requestURI,request);
    220             if (sipStack.isLoggingEnabled())
    221                 sipStack.getStackLogger()
    222                         .logDebug("Using request URI maddr to route the request = "
    223                                 + hop.toString());
    224 
    225             // JvB: don't remove it!
    226             // ((SipURI) requestURI).removeParameter("maddr");
    227 
    228             return hop;
    229 
    230         } else if (defaultRoute != null) {
    231             if (sipStack.isLoggingEnabled())
    232                 sipStack.getStackLogger()
    233                         .logDebug("Using outbound proxy to route the request = "
    234                                 + defaultRoute.toString());
    235             return defaultRoute;
    236         } else if (requestURI.isSipURI()) {
    237             Hop hop = createHop((SipURI) requestURI,request);
    238             if (hop != null && sipStack.isLoggingEnabled())
    239                 sipStack.getStackLogger().logDebug("Used request-URI for nextHop = "
    240                         + hop.toString());
    241             else if (sipStack.isLoggingEnabled()) {
    242                 sipStack.getStackLogger()
    243                         .logDebug("returning null hop -- loop detected");
    244             }
    245             return hop;
    246 
    247         } else {
    248             // The internal router should never be consulted for non-sip URIs.
    249             InternalErrorHandler.handleException("Unexpected non-sip URI",
    250                     this.sipStack.getStackLogger());
    251             return null;
    252         }
    253 
    254     }
    255 
    256     /**
    257      * Performs strict router fix according to RFC3261 section 16.6 step 6
    258      *
    259      * pre: top route header in request has no 'lr' parameter in URI post:
    260      * request-URI added as last route header, new req-URI = top-route-URI
    261      */
    262     public void fixStrictRouting(SIPRequest req) {
    263 
    264         RouteList routes = req.getRouteHeaders();
    265         Route first = (Route) routes.getFirst();
    266         SipUri firstUri = (SipUri) first.getAddress().getURI();
    267         routes.removeFirst();
    268 
    269         // Add request-URI as last Route entry
    270         AddressImpl addr = new AddressImpl();
    271         addr.setAddess(req.getRequestURI()); // don't clone it
    272         Route route = new Route(addr);
    273 
    274         routes.add(route); // as last one
    275         req.setRequestURI(firstUri);
    276         if (sipStack.isLoggingEnabled()) {
    277             sipStack.getStackLogger().logDebug("post: fixStrictRouting" + req);
    278         }
    279     }
    280 
    281     /**
    282      * Utility method to create a hop from a SIP URI
    283      *
    284      * @param sipUri
    285      * @return
    286      */
    287 
    288 
    289     private final Hop createHop(SipURI sipUri, Request request) {
    290         // always use TLS when secure
    291         String transport = sipUri.isSecure() ? SIPConstants.TLS : sipUri
    292                 .getTransportParam();
    293         if (transport == null) {
    294             //@see issue 131
    295             ViaHeader via = (ViaHeader) request.getHeader(ViaHeader.NAME);
    296             transport = via.getTransport();
    297         }
    298 
    299         // sipUri.removeParameter("transport");
    300 
    301         int port;
    302         if (sipUri.getPort() != -1) {
    303             port = sipUri.getPort();
    304         } else {
    305             if (transport.equalsIgnoreCase(SIPConstants.TLS))
    306                 port = 5061;
    307             else
    308                 port = 5060; // TCP or UDP
    309         }
    310         String host = sipUri.getMAddrParam() != null ? sipUri.getMAddrParam()
    311                 : sipUri.getHost();
    312         AddressResolver addressResolver = this.sipStack.getAddressResolver();
    313         return addressResolver
    314                 .resolveAddress(new HopImpl(host, port, transport));
    315 
    316     }
    317 
    318     /**
    319      * Get the default hop.
    320      *
    321      * @return defaultRoute is the default route. public java.util.Iterator
    322      *         getDefaultRoute(Request request) { return
    323      *         this.getNextHops((SIPRequest)request); }
    324      */
    325 
    326     public javax.sip.address.Hop getOutboundProxy() {
    327         return this.defaultRoute;
    328     }
    329 
    330     /*
    331      * (non-Javadoc)
    332      *
    333      * @see javax.sip.address.Router#getNextHop(javax.sip.message.Request)
    334      */
    335     public ListIterator getNextHops(Request request) {
    336         try {
    337             LinkedList llist = new LinkedList();
    338             llist.add(this.getNextHop(request));
    339             return llist.listIterator();
    340         } catch (SipException ex) {
    341             return null;
    342         }
    343 
    344     }
    345 }
    346