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/registrar/Registrar.java


1   package com.opencloud.slee.services.sip.registrar;
2   
3   import java.util.ArrayList;
4   import java.util.GregorianCalendar;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.ListIterator;
8   import java.util.Map;
9   
10  import javax.sip.ServerTransaction;
11  import javax.sip.address.Address;
12  import javax.sip.address.URI;
13  import javax.sip.header.CSeqHeader;
14  import javax.sip.header.CallIdHeader;
15  import javax.sip.header.ContactHeader;
16  import javax.sip.header.DateHeader;
17  import javax.sip.header.ExpiresHeader;
18  import javax.sip.header.Header;
19  import javax.sip.header.HeaderAddress;
20  import javax.sip.header.ToHeader;
21  import javax.sip.message.Request;
22  import javax.sip.message.Response;
23  import javax.slee.Sbb;
24  
25  
26  
27  /**
28   * 
29   * TODO Class Description
30   * 
31   * @author F.Moggia
32   */
33  public class Registrar{
34      private LocationService ls;
35      private long minExpires = 10; // seconds
36      private long maxExpires = 7200; // seconds
37      
38      private String[] localDomainNames = {"nist.gov", "antd.nist.gov", "nist.gov;transport=udp"};
39      private RegistrarSbb sbb;
40      
41      public Registrar(com.opencloud.slee.services.sip.registrar.RegistrarSbb sbb) {
42          this.sbb = sbb;
43          ls = new LocationService();
44          
45      }
46      
47      public String getDomain(URI uri) {
48          String address = uri.toString();
49  
50          // get rid of user part
51          int index = address.indexOf('@');
52          if (index != -1) address = address.substring(index+1);
53  
54          // get rid of protocol part
55          index = address.indexOf(':');
56          if (index != -1) address = address.substring(index+1);
57  
58          // get rid of port and all that comes after
59          index = address.indexOf(':');
60          if (index != -1) address = address.substring(0, index);
61  
62          return address;
63      }
64      
65      private LocationService getLocationService(){
66          return ls;
67      }
68      
69      public boolean isLocalDomain(URI uri) {
70          /*boolean match = false;
71          String uriDomain = getDomain(uri);
72          for (int i = 0; i < localDomainNames.length; i++ ) {
73              match = localDomainNames[i].equalsIgnoreCase(uriDomain);
74              if (match) break;
75          }
76  
77          return match;*/
78          return true;
79      }
80      
81      public static String getCanonicalAddress(HeaderAddress header) {
82          Address na = header.getAddress();
83  
84          URI uri = na.getURI();
85  
86          String addr = uri.toString();
87  
88          int index = addr.indexOf(':');
89          index = addr.indexOf(':', index+1);
90          if (index != -1) {
91              // Get rid of the port
92              addr = addr.substring(0, index);
93          }
94  
95          return addr;
96      }
97      
98      public void processRequest(ServerTransaction txn, Request request) {
99  
100          // RFC3261 ch10.3
101          try {
102              
103              // Is this request for this domain?
104              if (!isLocalDomain(request.getRequestURI())) {
105                  // If we are a proxy then forward to correct domain
106                  // For now just return error code
107                  Response response = sbb.getMessageFactory().createResponse(Response.FORBIDDEN, request);
108                  txn.sendResponse(response);
109                  return;
110              }
111 
112              // Process require header
113 
114 
115              // Authenticate
116              // Authorize
117              // OK we're authorized now ;-)
118 
119              // extract address-of-record
120              String sipAddressOfRecord = getCanonicalAddress((HeaderAddress)request.getHeader(ToHeader.NAME));
121 
122              System.out.println("address-of-record = " + sipAddressOfRecord);
123 
124              // map will be empty if user not in LS...
125              // Note we don't care if the user has a valid account in the LS, we just
126              // add them anyway.
127              Map bindings = getLocationService().getBindings(sipAddressOfRecord);
128 
129              // Do we have any contact header(s)?
130              if (request.getHeader(ContactHeader.NAME) == null) {
131                  // Just send OK with current bindings - this request was a query.
132                  sendRegistrationOKResponse(txn, request, bindings);
133                  return;
134              }
135 
136              // Check contact, callid, cseq
137 
138              ArrayList newContacts = getContactHeaderList(request.getHeaders(ContactHeader.NAME));
139              ExpiresHeader expiresHeader = null;
140              boolean removeAll = false;
141              CallIdHeader cidh = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
142              String callId = cidh.getCallId();
143 
144              CSeqHeader cseqh = (CSeqHeader)request.getHeader(CSeqHeader.NAME);
145              long seq = cseqh.getSequenceNumber();
146 
147              expiresHeader = request.getExpires();
148 
149              if (hasWildCard(newContacts)) { // This is a "Contact: *" "remove all bindings" request
150                  if ((expiresHeader == null) || (expiresHeader.getExpires() != 0) || (newContacts.size() > 1)) {
151                      // malformed request in RFC3261 ch10.3 step 6
152                      txn.sendResponse(sbb.getMessageFactory().createResponse(Response.BAD_REQUEST, request));
153                      return;
154                  }
155 
156                  removeAll = true;
157              }
158 
159              
160             if (removeAll) {
161                 System.out.println("Removing bindings");
162                  // Go through list of current bindings
163                  // if callid doesn't match - remove binding
164                  // if callid matches and seq greater, remove binding.
165                  Iterator it = bindings.values().iterator();
166                  while (it.hasNext()) {
167                      RegistrationBinding binding = (RegistrationBinding) it.next();
168                      if (callId.equals(binding.getCallId())) {
169                          
170                         if (seq > binding.getCSeq()) {
171                             it.remove();
172                         }
173                         else {
174                             txn.sendResponse(sbb.getMessageFactory().createResponse(Response.BAD_REQUEST, request));
175                             return;
176                         }
177                      }
178                     else {
179                         it.remove();
180                     }
181                  }
182                  try {
183                     getLocationService().setBindings(sipAddressOfRecord, bindings);
184                  } catch (LocationServiceException lse) {
185                     txn.sendResponse(sbb.getMessageFactory().createResponse(Response.SERVER_INTERNAL_ERROR, request));
186                     return;
187                 }
188                 sendRegistrationOKResponse(txn, request, bindings);
189             }
190             else {
191                 // Update bindings
192                 System.out.println("Updating bindings");
193                 ListIterator li = newContacts.listIterator();
194 
195                 while (li.hasNext()) {
196                     ContactHeader contact = (ContactHeader) li.next();
197                     
198                     // get expires value, either in header or default
199                     // do min-expires etc
200                     long requestedExpires = 0;
201                     
202                     if (contact.getExpires() >= 0) {
203                         requestedExpires = contact.getExpires();
204                     }
205                     else if ((expiresHeader != null) && (expiresHeader.getExpires() >= 0)) {
206                         requestedExpires = expiresHeader.getExpires();
207                     }
208                     else {
209                         requestedExpires = 3600; //default
210                     }
211                     
212                     // If expires too large, reset to our local max
213                     if (requestedExpires > maxExpires) {
214                         requestedExpires = maxExpires;
215                     }
216                     else if ((requestedExpires > 0) && (requestedExpires < minExpires)) {
217                         // requested expiry too short, send response with min-expires
218                         // 
219                         sendIntervalTooBriefResponse(txn, request);
220                         return;
221                     }
222                                                             
223                     // Get the q-value (preference) for this binding - default to 0.0 (min)
224                     float q = 0;
225                     if (contact.getQValue() != -1) q = contact.getQValue();
226                     if ((q > 1) || (q < 0)) {
227                         txn.sendResponse(sbb.getMessageFactory().createResponse(Response.BAD_REQUEST, request));
228                         return;
229                     }
230                     
231                     // Find existing binding
232                     URI uri = contact.getAddress().getURI();
233                     String contactAddress = uri.toString();
234                     
235                     RegistrationBinding binding = (RegistrationBinding)bindings.get(contactAddress);
236                     
237                     if (binding != null) { // Update this binding
238                         if (callId.equals(binding.getCallId())) {
239                             if (seq <= binding.getCSeq()) {
240                                 // Invalid request
241                                 txn.sendResponse(sbb.getMessageFactory().createResponse(Response.BAD_REQUEST, request));
242                                 return;
243                             }
244                         }
245                         
246                         if (requestedExpires == 0) {
247                             System.out.println("Removing binding: " + sipAddressOfRecord + " -> " + contactAddress);
248                             bindings.remove(contactAddress);
249                         }
250                         else {
251                         
252                             System.out.println("Updating binding: " + sipAddressOfRecord + " -> " + contactAddress);
253                             System.out.println(contact.toString());
254                             binding.setCallId(callId);
255                             binding.setCSeq(seq);
256                             binding.setExpiryDelta(requestedExpires);
257                             binding.setQValue(q);
258                             // set timer for registration expiry
259                             setRegistrationTimer(sipAddressOfRecord, contactAddress, requestedExpires, callId, seq);
260                         }
261                         
262                     }
263                     else {
264                         // Create new binding, add to list...
265                         if (requestedExpires != 0) {
266                             System.out.println("Adding new binding: " + sipAddressOfRecord + " -> " + contactAddress);
267                             System.out.println(contact.toString());
268                             RegistrationBinding newBinding = new RegistrationBinding(contactAddress, "", requestedExpires, q, callId, seq);
269                             // removed comment parameter to registration binding - Address and Contact headers don't have comments in 1.1
270 
271                             bindings.put(contactAddress, newBinding);
272                             // set timer for registration expiry
273                             setRegistrationTimer(sipAddressOfRecord, contactAddress, requestedExpires, callId, seq);
274                        }
275                    }
276                 }
277                 // Update bindings, return 200 if successful, 500 on error
278 
279                 try {
280                     getLocationService().setBindings(sipAddressOfRecord, bindings);
281                 } catch (LocationServiceException lse) {
282                     lse.printStackTrace();
283                     txn.sendResponse(sbb.getMessageFactory().createResponse(Response.SERVER_INTERNAL_ERROR, request));
284                     return;
285                 }
286 
287                 sendRegistrationOKResponse(txn, request, bindings);
288                 
289             }
290 
291 
292          } catch (Exception e) {
293              e.printStackTrace();
294          }
295 
296      }
297           
298      private boolean hasWildCard(ArrayList contactHeaders) {
299          Iterator it = contactHeaders.iterator();
300          while (it.hasNext()) {
301              ContactHeader header = (ContactHeader) it.next();
302              if (header.toString().indexOf('*') > 0) return true;
303          }
304          return false;
305      }
306 
307      private ArrayList getContactHeaderList(ListIterator it) {
308          ArrayList l = new ArrayList();
309          try {
310              while (it.hasNext()) {
311                  l.add(it.next());
312              }
313          }catch (Exception e) {
314              System.err.println(e.toString());
315          }
316          return l;
317      }
318 
319 
320     /**
321      * Set a timer on a registration entry. If a timer is already set for this registration,
322      * reset it to the new timeout value
323      * @param sipAddress the public SIP address-of-record for the user
324      * @param sipContactAddress the physical contact address for this registration
325      * @param timeout expiry time (in seconds) of the registration
326      * @param callId the SIP callid of the REGISTER request
327      * @param cseq the SIP sequence number of the REGISTER request
328      */
329     void setRegistrationTimer(String sipAddress, String sipContactAddress, long timeout, String callId, long cseq) {
330         // leave empty since I am going to override this and use a SLEE timer anyway, but there could be a
331         // default impl here eg. using java.util.Timer
332     }
333 
334     /**
335      * Expire registration entry, remove it from location service. This would be a callback from
336      * whatever timer is set in setRegistrationExpiry() above. Only remove a registration if the
337      * callId and cseq values match those of the original registration. If the values don't match,
338      * this means the registration has been updated by a more recent REGISTER request, so we should
339      * not change anything. The timer for the most recent REGISTER request will expire the entry.
340      * @param sipAddress the public SIP address-of-record for the user
341      * @param sipContactAddress the physical contact address for this registration
342      * @param callId the SIP callid of the REGISTER request
343      * @param cseq the SIP sequence number of the REGISTER request
344      */
345     void expireRegistration(String sipAddress, String sipContactAddress, String callId, long cseq) {
346         // find user in location service
347         try {
348             Map bindings = getLocationService().getBindings(sipAddress);
349             if (bindings == null) {
350                 System.out.println("expireRegistration: user " + sipAddress + " not found.");
351                 return;
352             }
353 
354             // remove binding for sipContactAddress, if callId and cseq match.
355             RegistrationBinding binding = (RegistrationBinding) bindings.get(sipContactAddress);
356             if (binding == null) {
357                 System.out.println("expireRegistration: registration for " + sipAddress + " -> " + sipContactAddress + " not found.");
358                 return;
359             }
360 
361             if (callId.equals(binding.getCallId()) && (cseq == binding.getCSeq())) {
362                 // this is my registration, so I am allowed to remove it
363                 RegistrationBinding removedBinding = (RegistrationBinding) bindings.remove(sipContactAddress);
364 
365                 // set bindings
366                 getLocationService().setBindings(sipAddress, bindings);
367                 System.out.println("expireRegistration: removed binding: " + sipAddress + " -> " + sipContactAddress);
368             }
369             else { // not my registration, do nothing
370                 System.out.println("expireRegistration: callId/cseq for binding (" + sipAddress + " -> " + sipContactAddress + ") has been updated, not removing");
371             }
372 
373         } catch (LocationServiceException lse) {
374             lse.printStackTrace();
375         }
376 
377     }
378 
379 
380      private List getContactHeaders(Map bindings) {
381          if (bindings == null) return null;
382          ArrayList contactHeaders = new ArrayList();
383          Iterator it = bindings.values().iterator();
384          while (it.hasNext()) {
385             RegistrationBinding binding = (RegistrationBinding)it.next();
386             ContactHeader header = binding.getContactHeader(sbb.getAddressFactory(), sbb.getHeaderFactory());
387             if (header != null) {
388                 contactHeaders.add(header);
389             }
390          }
391          return contactHeaders;
392         
393      }
394      
395         
396      private void sendIntervalTooBriefResponse(ServerTransaction txn, Request request) {
397          try {
398             Response res = sbb.getMessageFactory().createResponse(423, request);  // In RFC3261 but not JAIN SIP - coming in JAIN SIP 1.1??
399             res.setReasonPhrase("Interval Too Brief");
400             DateHeader dateHeader = sbb.getHeaderFactory().createDateHeader(new GregorianCalendar());
401             res.setHeader(dateHeader);
402             Header minExpiresHeader = sbb.getHeaderFactory().createHeader("Min-Expires", String.valueOf(minExpires));
403             res.addHeader(minExpiresHeader);
404             System.out.println("Sending Response:\n" + res.toString());
405             txn.sendResponse(res);
406          } catch (Exception e) {
407              System.err.println(e.toString());
408              e.printStackTrace();
409          }
410          
411      }
412      
413      private void sendRegistrationOKResponse(ServerTransaction txn, Request request, Map bindings) {
414          List contactHeaders = getContactHeaders(bindings);
415          try {
416             Response res = sbb.getMessageFactory().createResponse(Response.OK, request);
417 
418             if ((contactHeaders != null) && (!contactHeaders.isEmpty())) {
419                 System.out.println("Adding "+contactHeaders.size()+" headers");
420                 for (int i = 0; i < contactHeaders.size(); i++) {
421                     ContactHeader hdr = (ContactHeader) contactHeaders.get(i);
422                     res.addHeader(hdr);
423                 }
424                 //     ((SIPResponse) res).setHeaders(contactHeaders);
425             }
426             DateHeader dateHeader = sbb.getHeaderFactory().createDateHeader(new GregorianCalendar());
427             res.setHeader(dateHeader);
428             System.out.println("Sending Response:\n" + res.toString());
429             txn.sendResponse(res);
430          } catch (Exception e) {
431             System.err.println(e.toString());
432             e.printStackTrace();
433          }
434      }
435 
436      public void processResponse(ServerTransaction txn, Response response) {
437      }     
438 }