• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DefaultRouter()105     private DefaultRouter() {
106 
107     }
108 
109     /**
110      * Constructor.
111      */
DefaultRouter(SipStack sipStack, String defaultRoute)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      */
getNextHop(Request request)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      */
fixStrictRouting(SIPRequest req)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 
createHop(SipURI sipUri, Request request)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 
getOutboundProxy()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      */
getNextHops(Request request)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