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 ******************************************************************************/
29package gov.nist.javax.sip.stack;
30
31import gov.nist.javax.sip.message.*;
32import gov.nist.javax.sip.address.*;
33import gov.nist.javax.sip.header.*;
34import gov.nist.javax.sip.*;
35import gov.nist.core.*;
36import gov.nist.core.net.AddressResolver;
37
38import javax.sip.*;
39import java.util.Iterator;
40import java.util.LinkedList;
41import java.util.ListIterator;
42
43import javax.sip.header.RouteHeader;
44import javax.sip.header.ViaHeader;
45import javax.sip.message.*;
46import 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 */
99public 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