Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/opencloud/slee/services/sip/common/AbstractSipProxy.java


1   package com.opencloud.slee.services.sip.common;
2   
3   
4   import javax.sip.message.Request;
5   import javax.sip.message.Response;
6   import javax.sip.address.URI;
7   import javax.sip.address.Address;
8   import javax.sip.address.SipURI;
9   import javax.sip.*;
10  import javax.sip.header.*;
11  
12  import com.opencloud.slee.services.sip.registrar.LocationServiceException;
13  import com.opencloud.slee.services.sip.registrar.RegistrationBinding;
14  
15  import java.util.*;
16  
17  /**
18   * A template class that does much of what a typical SIP proxy would do,
19   * according to RFC3261 section 16.
20   *
21   * Applications should override the methods called by processRequest and
22   * processResponse to implement specific features.
23   */
24  public abstract class AbstractSipProxy extends MessageHandler {
25          
26      public AbstractSipProxy(SipServerConfig config) {
27          super(config);
28      }
29      
30      /**
31       * This method defines the default behaviour for a proxy procesing a request.
32       * Implementations should override as appropriate to customize proxy behaviour.
33       *
34       * Steps in processing a request, from RFC3261:
35       * 16.3 Request Validation
36       * 16.4 Route Information Preprocessing
37       * 16.5 Determining Request Targets
38       * 16.6 Request Forwarding
39       *      1. Copy request
40       *      2. Set Request-URI
41       *      3. Decrement Max-Forwards
42       *      4. Add Record-Route
43       *      5. Add Additional Header Fields
44       *      6. Postprocess routing information
45       *      7. Determine Next-Hop Address, Port and Transport
46       *      8. Add a Via header field value
47       *      9. Add a Content-Leangth header field if necessary
48       *      10. Forward Request
49       *      11. Set timer C           
50       */
51      public void processRequest(ServerTransaction txn, Request request) {
52          //System.out.println("processRequest: tid = " + tid + ", request: \n" + request.toString());
53          try {
54              Request newRequest = (Request)request.clone();
55  
56              // 16.3 Request Validation
57              validateRequest(txn, newRequest);
58              
59              // 16.4 Route Information Preprocessing
60              routePreProcess(newRequest);
61              
62              // 16.5 Determining Request Targets
63              List targets = determineRequestTargets(newRequest);           
64              
65              Iterator it = targets.iterator();
66              while (it.hasNext()) {
67                  URI target = (URI)it.next();
68                  System.err.println("SIP Proxy Forwarding: " + request.getMethod() + " to URI target: "+target);
69                  // 16.6 Request Forwarding
70                  // 1. Copy request
71  
72                  // 2. Request-URI
73                  newRequest.setRequestURI(target);
74  
75                  // *NEW* CANCEL processing
76                  // CANCELs are hop-by-hop, so here must remove any existing Via headers,
77                  // Record-Route headers. We insert Via header below so we will get response.
78                  if (newRequest.getMethod().equals(Request.CANCEL)) {
79                      newRequest.removeHeader(ViaHeader.NAME);
80                      newRequest.removeHeader(RecordRouteHeader.NAME);
81                  }
82                  else {
83                      // 3. Max-Forwards
84                      decrementMaxForwards(newRequest);
85                      // 4. Record-Route
86                      addRecordRouteHeader(newRequest);
87                  }
88  
89                  // 5. Add Additional Header Fields
90                  // TBD
91                  // 6. Postprocess routing information
92                  // TBD
93                  // 7. Determine Next-Hop Address, Port and Transport
94                  // TBD
95  
96                  // 8. Add a Via header field value
97                  addViaHeader(newRequest);
98  
99                  // 9. Add a Content-Leangth header field if necessary
100                 //TBD
101 
102                 // 10. Forward Request
103                 forwardRequest(txn, newRequest);
104 
105                 // 11. Set timer C
106                 // TBD
107             }
108             
109         } catch (SipSendErrorResponseException se) {
110             int statusCode = se.getStatusCode();
111             sendErrorResponse(txn, request, statusCode);
112         } catch (Exception e) {
113             e.printStackTrace();
114         }
115     }
116     
117     public void sendErrorResponse(ServerTransaction txn, Request request, int statusCode) {
118         try {
119             Response response = config.getMessageFactory().createResponse(statusCode, request);
120             txn.sendResponse(response);
121         } catch (Exception e) {
122             e.printStackTrace();
123         }
124     }
125     
126     /**
127      * Forwards request on to destination. This method should be overridden to
128      * preserve state (eg. client txn -> server txn mapping) by proxy implementations
129      */
130     public void forwardRequest(ServerTransaction txn, Request request) {
131         // simplest case - just send request - no state saved
132         try {
133             config.getSipProvider().sendRequest(request);
134         } catch (Exception e) {
135             e.printStackTrace();
136         }
137     }
138     
139     /** 
140      * Performs request validation as per RFC 3261 section 16.3. If a request fails
141      * validation, throw exception to cause appropriate error response to client.
142      *
143      * @param txn the server transaction of the request.
144      * @param request the SIP request to be validated.
145      */
146     public void validateRequest(ServerTransaction txn, Request request) throws SipSendErrorResponseException {
147         // 1. Reasonable syntax
148 
149         // 2. URI scheme
150         URI requestURI = null; 
151         requestURI = request.getRequestURI();
152 
153         boolean supportedURIScheme = false;
154         supportedURIScheme = isSupportedURIScheme(requestURI);
155 
156         if (!supportedURIScheme) {
157             throw new SipSendErrorResponseException("Unsupported URI scheme", Response.UNSUPPORTED_URI_SCHEME);
158         }
159 
160         // 3. Max-Forwards
161         checkMaxForwards(txn, request);
162 
163         // 4. Loop Detection - TBD
164         // 5. Proxy-Require - TBD
165         // 6. Proxy-Authorization - TBD
166     }
167 
168     /**
169      * Validate the max-forwards header throw a user error exception (too many hops) if max-forwards reaches 0.
170      * @param txn
171      * @param request
172      * @throws SipSendErrorResponseException
173      */
174     public void checkMaxForwards(ServerTransaction txn, Request request) throws SipSendErrorResponseException {
175         MaxForwardsHeader mfh = (MaxForwardsHeader) request.getHeader(MaxForwardsHeader.NAME);
176         if (mfh == null) return;
177 
178         int maxForwards = 0;
179         maxForwards = ((MaxForwardsHeader) request.getHeader(MaxForwardsHeader.NAME)).getMaxForwards();
180 
181         if (maxForwards > 0) {
182             return;
183         }
184         else {
185             // MAY respond to OPTIONS, otherwise return 483 Too Many Hops
186             throw new SipSendErrorResponseException("Too many hops", Response.TOO_MANY_HOPS);
187         }
188 
189     }
190 
191     /**
192      * Attempts to find a locally registered contact address for the given URI, using
193      * the location service interface.
194      */
195     public URI findLocalTarget(URI uri) throws SipSendErrorResponseException {
196         String addressOfRecord = uri.toString();
197 
198         Map bindings = null;
199         try {
200             bindings = config.getLocationService().getBindings(addressOfRecord);
201         } catch (LocationServiceException lse) {
202             lse.printStackTrace();
203         }
204 
205         if (bindings == null) {
206             throw new SipSendErrorResponseException("User not found", Response.NOT_FOUND);
207         }
208         if (bindings.isEmpty()) {
209             throw new SipSendErrorResponseException("User temporarily unavailable", Response.TEMPORARILY_UNAVAILABLE);
210         }
211         
212         Iterator it = bindings.values().iterator();
213         URI target = null;
214         while (it.hasNext()) {
215             RegistrationBinding binding = (RegistrationBinding)it.next();
216             System.out.println("BINDINGS: " + binding);
217             ContactHeader header = binding.getContactHeader(config.getAddressFactory(), config.getHeaderFactory());
218             System.out.println("CONTACT HEADER: " + header);
219             if (header == null) { // entry expired
220                 continue;  // see if there are any more contacts...
221             }
222             Address na = header.getAddress();
223             System.out.println("Address: " + na);
224             target = na.getURI();
225             break;
226         }    
227         if (target == null) {
228             System.err.println("findLocalTarget: No contacts for " + addressOfRecord + " found.");
229             throw new SipSendErrorResponseException("User temporarily unavailable", Response.TEMPORARILY_UNAVAILABLE);
230         }
231         return target;
232     }
233    
234     /**
235      * Adds a default Via header to the request. Override to provide a different
236      * Via header.
237      */
238     public void addViaHeader(Request request) {
239         try {
240             ViaHeader via = config.getHeaderFactory().createViaHeader(config.getHostname(), config.getPort(), "UDP", null);
241             request.addHeader(via);
242         
243         } catch (Exception e) {
244             e.printStackTrace();
245         }
246         
247     }    
248         
249     /**
250      * Adds a default Record-Route header to the request. Override to provide a different
251      * Record-Route header.
252      */
253     public void addRecordRouteHeader(Request request) {
254         try {
255             // Add our record-route header to list...
256             SipURI myURI = config.getAddressFactory().createSipURI(null, config.getHostname());
257             myURI.setPort(config.getPort());
258             myURI.setMAddrParam(config.getHostname());
259             myURI.setTransportParam(config.getTransport());
260             Address myName = config.getAddressFactory().createAddress(myURI);
261             RecordRouteHeader myHeader = config.getHeaderFactory().createRecordRouteHeader(myName);
262             request.addHeader(myHeader);
263 
264         } catch (Exception e) {
265             e.printStackTrace();
266         }
267         
268     }
269 
270     /**
271      * Decrement the value of max-forwards.  If no max-forwards header present, create a max-forwards header with the
272      * RFC3261 recommended default value
273      * @param request
274      * @throws SipSendErrorResponseException
275      */
276     public void decrementMaxForwards(Request request) throws SipSendErrorResponseException {
277         MaxForwardsHeader max = (MaxForwardsHeader) request.getHeader(MaxForwardsHeader.NAME);
278         try {
279             if (max == null) {
280                 // add max-forwards with default 70 hops
281                 max = config.getHeaderFactory().createMaxForwardsHeader(70);
282                 request.setHeader(max);
283             }
284             else {
285                 // decrement max-forwards
286                 int maxForwards = max.getMaxForwards();              
287                 maxForwards--;
288                 max.setMaxForwards(maxForwards);
289                 request.setHeader(max);
290             }
291         } catch (Exception e) {
292             throw new SipSendErrorResponseException("Error updating max-forwards", Response.SERVER_INTERNAL_ERROR);
293         }   
294     }
295 
296     /**
297      * Check for strict-routing style route headers and swap with Request-URI if 
298      * applicable.
299      */
300     public void routePreProcess(Request request) throws SipSendErrorResponseException {
301         URI requestURI = null;
302         requestURI = request.getRequestURI();
303 
304         if ((requestURI.isSipURI()) && 
305       (((SipURI)requestURI).getUser() == null) && 
306       (((SipURI)requestURI).getHost().equalsIgnoreCase(config.getHostname()))) {
307             // client is a strict router, replace request-URI with last
308             // value in Route header field...
309             try {
310                 ListIterator it = request.getHeaders(RouteHeader.NAME);
311                 LinkedList l = new LinkedList();
312                 // need last value in list
313                 while (it.hasNext()) {
314                     RouteHeader r = (RouteHeader)it.next();
315                     l.add(r);
316                 }
317                 RouteHeader route = (RouteHeader)l.getLast();
318 
319                 l.removeLast();    // Remove the last route header from the list,  possibly leaving an empty list
320 
321                 request.removeHeader(RouteHeader.NAME);    // Remove all route headers from the message
322 
323                 // Re-add the remaining headers to the message
324                 for (int i = 0; i < l.size(); i++) {
325                     RouteHeader routeHeader = (RouteHeader) l.get(i);
326                     request.addHeader(routeHeader);
327                 }
328 
329                 URI newURI = route.getAddress().getURI();
330                 request.setRequestURI(newURI);
331 
332             } catch (Exception e) {
333                 e.printStackTrace();
334                 throw new SipSendErrorResponseException("Error updating route headers", Response.SERVER_INTERNAL_ERROR);
335 
336             }
337         } 
338         // From RFC3261 16.4:
339         // If the first value in the Route header field indicates this proxy, 
340         // the proxy MUST remove that value from the request. 
341         Iterator routeHeaders = request.getHeaders(RouteHeader.NAME);
342         if (routeHeaders.hasNext()) {
343             RouteHeader r = (RouteHeader) routeHeaders.next();
344             // is this route header for our hostname & port?
345             URI uri = r.getAddress().getURI();
346             if (uri.isSipURI()) {
347                 SipURI sipURI = (SipURI) uri;
348                 int uriPort = sipURI.getPort();
349                 if (uriPort <= 0) uriPort = 5060; // WARNING: Assumes stack impl returns <= 0 if port not specified in URI
350                 // JAIN SIP does not specify but NIST SIP returns -1
351                 if ((sipURI.getHost().equalsIgnoreCase(config.getHostname())) &&
352                     (uriPort == config.getPort())) {
353                     // remove this route header
354                     routeHeaders.remove();
355                     // if this was the last one, remove the header entirely (why isn't this automatic?)
356                     if (!routeHeaders.hasNext()) request.removeHeader(RouteHeader.NAME);
357                 }
358             }
359         }
360             
361 
362     }
363 
364     /**
365      * Determines target SIP URI(s) for request, using location service or
366      * other criteria.
367      *
368      * TODO: Forking (return more than one target)
369      *
370      * @param request the SIP request being forwarded
371      * @return a list of URIs
372      */
373     public List determineRequestTargets(Request request) throws SipSendErrorResponseException {
374         LinkedList targets = new LinkedList();
375         
376         URI requestURI = null; 
377         URI target = null;
378         boolean localDomain = false;
379         requestURI = request.getRequestURI();
380         localDomain = isLocalDomain(requestURI);
381 
382         if (localDomain) {
383             // determine local SIP target(s) using location service etc
384             target = findLocalTarget(requestURI);
385             if (target == null) { // not found (or not currently registered)
386                 throw new SipSendErrorResponseException("User not registered", Response.TEMPORARILY_UNAVAILABLE);
387             }
388         }
389         else {
390             // destination addr is outside our domain
391             target = requestURI;
392         }
393         targets.add(target);
394         return targets;
395     }
396     
397     /**
398      * Generic proxy response processing
399      */
400     public void processResponse(ServerTransaction serverTransaction, Response response) {
401         
402         try {
403         
404       Response newResponse = (Response)response.clone();
405         
406             // 16.7 Response Processing
407             // 1. Find appropriate response context
408         
409             // 2. Update timer C for provisional responses
410         
411             // 3. Remove topmost via
412             Iterator viaHeaderIt = newResponse.getHeaders(ViaHeader.NAME);
413             viaHeaderIt.next();
414             viaHeaderIt.remove();
415             if (!viaHeaderIt.hasNext()) return; // response was meant for this proxy
416 
417             // 4. Add the response to the response context
418         
419             // 5. Check to see if this response should be forwarded immediately
420             if (newResponse.getStatusCode() == Response.TRYING) {
421                 return;
422             }
423         
424             // 6. When necessary, choose the best final response from the response context
425         
426             // 7. Aggregate authorization header fields if necessary
427         
428             // 8. Optionally rewrite Record-Route header field values
429         
430             // 9. Forward the response
431             forwardResponse(serverTransaction, newResponse);
432         
433             // 10. Generate any necessary CANCEL requests
434         
435         } catch (Exception e) {
436             e.printStackTrace();
437         }
438     }
439     
440     public void forwardResponse(ServerTransaction txn, Response response) {
441         try {
442             if (txn != null) {
443                 txn.sendResponse(response);
444             } 
445             else {
446                 // forward statelessly anyway
447                 config.getSipProvider().sendResponse(response);
448             }
449         } catch (Exception e) {
450             e.printStackTrace();
451         }
452     }
453     
454     
455 }