346 lines
13 KiB
Java
346 lines
13 KiB
Java
/*
|
|
* 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 gov.nist.javax.sip.stack;
|
|
|
|
import gov.nist.javax.sip.message.*;
|
|
import gov.nist.javax.sip.address.*;
|
|
import gov.nist.javax.sip.header.*;
|
|
import gov.nist.javax.sip.*;
|
|
import gov.nist.core.*;
|
|
import gov.nist.core.net.AddressResolver;
|
|
|
|
import javax.sip.*;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.ListIterator;
|
|
|
|
import javax.sip.header.RouteHeader;
|
|
import javax.sip.header.ViaHeader;
|
|
import javax.sip.message.*;
|
|
import javax.sip.address.*;
|
|
|
|
/*
|
|
* 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>javax.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>javax.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 javax.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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 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 javax.sip.address.Hop getOutboundProxy() {
|
|
return this.defaultRoute;
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see javax.sip.address.Router#getNextHop(javax.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;
|
|
}
|
|
|
|
}
|
|
}
|