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