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 package gov.nist.javax.sip.stack;
27 
28 import gov.nist.core.Host;
29 import gov.nist.core.HostPort;
30 import gov.nist.core.ServerLogger;
31 import gov.nist.core.StackLogger;
32 import gov.nist.core.ThreadAuditor;
33 import gov.nist.core.net.AddressResolver;
34 import gov.nist.core.net.DefaultNetworkLayer;
35 import gov.nist.core.net.NetworkLayer;
36 import gov.nist.javax.sip.DefaultAddressResolver;
37 import gov.nist.javax.sip.ListeningPointImpl;
38 import gov.nist.javax.sip.LogRecordFactory;
39 import gov.nist.javax.sip.SIPConstants;
40 import gov.nist.javax.sip.SipListenerExt;
41 import gov.nist.javax.sip.SipProviderImpl;
42 import gov.nist.javax.sip.SipStackImpl;
43 import gov.nist.javax.sip.header.Event;
44 import gov.nist.javax.sip.header.Via;
45 import gov.nist.javax.sip.header.extensions.JoinHeader;
46 import gov.nist.javax.sip.header.extensions.ReplacesHeader;
47 import gov.nist.javax.sip.message.SIPMessage;
48 import gov.nist.javax.sip.message.SIPRequest;
49 import gov.nist.javax.sip.message.SIPResponse;
50 
51 import java.io.IOException;
52 import java.net.InetAddress;
53 import java.net.SocketAddress;
54 import java.net.UnknownHostException;
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import java.util.LinkedList;
60 import java.util.Set;
61 import java.util.Timer;
62 import java.util.concurrent.ConcurrentHashMap;
63 import java.util.concurrent.atomic.AtomicInteger;
64 
65 import javax.sip.ClientTransaction;
66 import javax.sip.Dialog;
67 import javax.sip.DialogState;
68 import javax.sip.DialogTerminatedEvent;
69 import javax.sip.ServerTransaction;
70 import javax.sip.SipException;
71 import javax.sip.SipListener;
72 import javax.sip.TransactionState;
73 import javax.sip.TransactionTerminatedEvent;
74 import javax.sip.address.Hop;
75 import javax.sip.address.Router;
76 import javax.sip.header.CallIdHeader;
77 import javax.sip.header.EventHeader;
78 import javax.sip.message.Request;
79 import javax.sip.message.Response;
80 
81 /*
82  * Jeff Keyser : architectural suggestions and contributions. Pierre De Rop and Thomas Froment :
83  * Bug reports. Jeyashankher < jai@lucent.com > : bug reports. Jeroen van Bemmel : Bug fixes.
84  *
85  *
86  */
87 
88 /**
89  *
90  * This is the sip stack. It is essentially a management interface. It manages the resources for
91  * the JAIN-SIP implementation. This is the structure that is wrapped by the SipStackImpl.
92  *
93  * @see gov.nist.javax.sip.SipStackImpl
94  *
95  * @author M. Ranganathan <br/>
96  *
97  * @version 1.2 $Revision: 1.141 $ $Date: 2009/12/17 23:38:27 $
98  */
99 public abstract class SIPTransactionStack implements SIPTransactionEventListener, SIPDialogEventListener {
100 
101     /*
102      * Number of milliseconds between timer ticks (500).
103      */
104     public static final int BASE_TIMER_INTERVAL = 500;
105 
106     /*
107      * Connection linger time (seconds) this is the time (in seconds) for which we linger the TCP
108      * connection before closing it.
109      */
110     public static final int CONNECTION_LINGER_TIME = 8;
111 
112     /*
113      * Table of retransmission Alert timers.
114      */
115     protected ConcurrentHashMap<String, SIPServerTransaction> retransmissionAlertTransactions;
116 
117     // Table of early dialogs ( to keep identity mapping )
118     protected ConcurrentHashMap<String, SIPDialog> earlyDialogTable;
119 
120     // Table of dialogs.
121     protected ConcurrentHashMap<String, SIPDialog> dialogTable;
122 
123     // A set of methods that result in dialog creations.
124     protected static final Set<String> dialogCreatingMethods = new HashSet<String>();
125 
126     // Global timer. Use this for all timer tasks.
127 
128     private Timer timer;
129 
130     // List of pending server transactions
131     private ConcurrentHashMap<String, SIPServerTransaction> pendingTransactions;
132 
133     // hashtable for fast lookup
134     private ConcurrentHashMap<String, SIPClientTransaction> clientTransactionTable;
135 
136     // Set to false if you want hiwat and lowat to be consulted.
137     protected boolean unlimitedServerTransactionTableSize = true;
138 
139     // Set to false if you want unlimited size of client trnansactin table.
140     protected boolean unlimitedClientTransactionTableSize = true;
141 
142     // High water mark for ServerTransaction Table
143     // after which requests are dropped.
144     protected int serverTransactionTableHighwaterMark = 5000;
145 
146     // Low water mark for Server Tx table size after which
147     // requests are selectively dropped
148     protected int serverTransactionTableLowaterMark = 4000;
149 
150     // Hiwater mark for client transaction table. These defaults can be
151     // overriden by stack
152     // configuration.
153     protected int clientTransactionTableHiwaterMark = 1000;
154 
155     // Low water mark for client tx table.
156     protected int clientTransactionTableLowaterMark = 800;
157 
158     private AtomicInteger activeClientTransactionCount = new AtomicInteger(0);
159 
160     // Hashtable for server transactions.
161     private ConcurrentHashMap<String, SIPServerTransaction> serverTransactionTable;
162 
163     // A table of ongoing transactions indexed by mergeId ( for detecting merged
164     // requests.
165     private ConcurrentHashMap<String, SIPServerTransaction> mergeTable;
166 
167     private ConcurrentHashMap<String,SIPServerTransaction> terminatedServerTransactionsPendingAck;
168 
169     private ConcurrentHashMap<String,SIPClientTransaction> forkedClientTransactionTable;
170 
171     /*
172      * A wrapper around differnt logging implementations (log4j, commons logging, slf4j, ...) to help log debug.
173      */
174     private StackLogger stackLogger;
175 
176     /*
177      * ServerLog is used just for logging stack message tracecs.
178      */
179     protected ServerLogger serverLogger;
180 
181     /*
182      * We support UDP on this stack.
183      */
184     boolean udpFlag;
185 
186     /*
187      * Internal router. Use this for all sip: request routing.
188      *
189      */
190     protected DefaultRouter defaultRouter;
191 
192     /*
193      * Global flag that turns logging off
194      */
195     protected boolean needsLogging;
196 
197     /*
198      * Flag used for testing TI, bypasses filtering of ACK to non-2xx
199      */
200     private boolean non2XXAckPassedToListener;
201 
202     /*
203      * Class that handles caching of TCP/TLS connections.
204      */
205     protected IOHandler ioHandler;
206 
207     /*
208      * Flag that indicates that the stack is active.
209      */
210     protected boolean toExit;
211 
212     /*
213      * Name of the stack.
214      */
215     protected String stackName;
216 
217     /*
218      * IP address of stack -- this can be re-written by stun.
219      *
220      * @deprecated
221      */
222     protected String stackAddress;
223 
224     /*
225      * INET address of stack (cached to avoid repeated lookup)
226      *
227      * @deprecated
228      */
229     protected InetAddress stackInetAddress;
230 
231     /*
232      * Request factory interface (to be provided by the application)
233      */
234     protected StackMessageFactory sipMessageFactory;
235 
236     /*
237      * Router to determine where to forward the request.
238      */
239     protected javax.sip.address.Router router;
240 
241     /*
242      * Number of pre-allocated threads for processing udp messages. -1 means no preallocated
243      * threads ( dynamically allocated threads).
244      */
245     protected int threadPoolSize;
246 
247     /*
248      * max number of simultaneous connections.
249      */
250     protected int maxConnections;
251 
252     /*
253      * Close accept socket on completion.
254      */
255     protected boolean cacheServerConnections;
256 
257     /*
258      * Close connect socket on Tx termination.
259      */
260     protected boolean cacheClientConnections;
261 
262     /*
263      * Use the user supplied router for all out of dialog requests.
264      */
265     protected boolean useRouterForAll;
266 
267     /*
268      * Max size of message that can be read from a TCP connection.
269      */
270     protected int maxContentLength;
271 
272     /*
273      * Max # of headers that a SIP message can contain.
274      */
275     protected int maxMessageSize;
276 
277     /*
278      * A collection of message processors.
279      */
280     private Collection<MessageProcessor> messageProcessors;
281 
282     /*
283      * Read timeout on TCP incoming sockets -- defines the time between reads for after delivery
284      * of first byte of message.
285      */
286     protected int readTimeout;
287 
288     /*
289      * The socket factory. Can be overriden by applications that want direct access to the
290      * underlying socket.
291      */
292 
293     protected NetworkLayer networkLayer;
294 
295     /*
296      * Outbound proxy String ( to be handed to the outbound proxy class on creation).
297      */
298     protected String outboundProxy;
299 
300     protected String routerPath;
301 
302     // Flag to indicate whether the stack will provide dialog
303     // support.
304     protected boolean isAutomaticDialogSupportEnabled;
305 
306     // The set of events for which subscriptions can be forked.
307 
308     protected HashSet<String> forkedEvents;
309 
310     // Generate a timestamp header for retransmitted requests.
311     protected boolean generateTimeStampHeader;
312 
313     protected AddressResolver addressResolver;
314 
315     // Max time that the listener is allowed to take to respond to a
316     // request. Default is "infinity". This property allows
317     // containers to defend against buggy clients (that do not
318     // want to respond to requests).
319     protected int maxListenerResponseTime;
320 
321 
322     // A flag that indicates whether or not RFC 2543 clients are fully supported.
323     // If this is set to true, then To tag checking on the Dialog layer is
324     // disabled in a few places - resulting in possible breakage of forked dialogs.
325     protected boolean rfc2543Supported = true;
326 
327     // / Provides a mechanism for applications to check the health of threads in
328     // the stack
329     protected ThreadAuditor threadAuditor = new ThreadAuditor();
330 
331     protected LogRecordFactory logRecordFactory;
332 
333     // Set to true if the client CANCEL transaction should be checked before sending
334     // it out.
335     protected boolean cancelClientTransactionChecked = true;
336 
337     // Is to tag reassignment allowed.
338     protected boolean remoteTagReassignmentAllowed = true;
339 
340     protected boolean logStackTraceOnMessageSend = true;
341 
342     // Receive UDP buffer size
343     protected int receiveUdpBufferSize;
344 
345     // Send UDP buffer size
346     protected int sendUdpBufferSize;
347 
348     protected boolean stackDoesCongestionControl = true;
349 
350     protected boolean isBackToBackUserAgent = false;
351 
352     protected boolean checkBranchId;
353 
354 	protected boolean isAutomaticDialogErrorHandlingEnabled = true;
355 
356 	protected boolean isDialogTerminatedEventDeliveredForNullDialog = false;
357 
358 	// Max time for a forked response to arrive. After this time, the original dialog
359 	// is not tracked. If you want to track the original transaction you need to specify
360 	// the max fork time with a stack init property.
361 	protected int maxForkTime = 0;
362 
363 
364     // / Timer to regularly ping the thread auditor (on behalf of the timer
365     // thread)
366     class PingTimer extends SIPStackTimerTask {
367         // / Timer thread handle
368         ThreadAuditor.ThreadHandle threadHandle;
369 
370         // / Constructor
PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle)371         public PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle) {
372             threadHandle = a_oThreadHandle;
373         }
374 
runTask()375         protected void runTask() {
376             // Check if we still have a timer (it may be null after shutdown)
377             if (getTimer() != null) {
378                 // Register the timer task if we haven't done so
379                 if (threadHandle == null) {
380                     // This happens only once since the thread handle is passed
381                     // to the next scheduled ping timer
382                     threadHandle = getThreadAuditor().addCurrentThread();
383                 }
384 
385                 // Let the thread auditor know that the timer task is alive
386                 threadHandle.ping();
387 
388                 // Schedule the next ping
389                 getTimer().schedule(new PingTimer(threadHandle),
390                         threadHandle.getPingIntervalInMillisecs());
391             }
392         }
393 
394     }
395 
396 
397     class RemoveForkedTransactionTimerTask extends SIPStackTimerTask {
398 
399         private SIPClientTransaction clientTransaction;
400 
RemoveForkedTransactionTimerTask(SIPClientTransaction sipClientTransaction )401         public RemoveForkedTransactionTimerTask(SIPClientTransaction sipClientTransaction ) {
402             this.clientTransaction = sipClientTransaction;
403         }
404 
405         @Override
runTask()406         protected void runTask() {
407            forkedClientTransactionTable.remove(clientTransaction.getTransactionId());
408         }
409 
410     }
411 
412     static {
413     	// Standard set of methods that create dialogs.
414     	dialogCreatingMethods.add(Request.REFER);
415         dialogCreatingMethods.add(Request.INVITE);
416         dialogCreatingMethods.add(Request.SUBSCRIBE);
417     }
418 
419     /**
420      * Default constructor.
421      */
SIPTransactionStack()422     protected SIPTransactionStack() {
423         this.toExit = false;
424         this.forkedEvents = new HashSet<String>();
425         // set of events for which subscriptions can be forked.
426         // Set an infinite thread pool size.
427         this.threadPoolSize = -1;
428         // Close response socket after infinte time.
429         // for max performance
430         this.cacheServerConnections = true;
431         // Close the request socket after infinite time.
432         // for max performance
433         this.cacheClientConnections = true;
434         // Max number of simultaneous connections.
435         this.maxConnections = -1;
436         // Array of message processors.
437         messageProcessors = new ArrayList<MessageProcessor>();
438         // Handle IO for this process.
439         this.ioHandler = new IOHandler(this);
440 
441         // The read time out is infinite.
442         this.readTimeout = -1;
443 
444         this.maxListenerResponseTime = -1;
445 
446         // The default (identity) address lookup scheme
447 
448         this.addressResolver = new DefaultAddressResolver();
449 
450         // Notify may or may not create a dialog. This is handled in
451         // the code.
452         // Create the transaction collections
453 
454         // Dialog dable.
455         this.dialogTable = new ConcurrentHashMap<String, SIPDialog>();
456         this.earlyDialogTable = new ConcurrentHashMap<String, SIPDialog>();
457 
458         clientTransactionTable = new ConcurrentHashMap<String, SIPClientTransaction>();
459         serverTransactionTable = new ConcurrentHashMap<String, SIPServerTransaction>();
460         this.terminatedServerTransactionsPendingAck = new ConcurrentHashMap<String, SIPServerTransaction>();
461         mergeTable = new ConcurrentHashMap<String, SIPServerTransaction>();
462         retransmissionAlertTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
463 
464         // Start the timer event thread.
465 
466         this.timer = new Timer();
467         this.pendingTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
468 
469 
470         this.forkedClientTransactionTable = new ConcurrentHashMap<String,SIPClientTransaction>();
471 
472         if (getThreadAuditor().isEnabled()) {
473             // Start monitoring the timer thread
474             timer.schedule(new PingTimer(null), 0);
475         }
476     }
477 
478     /**
479      * Re Initialize the stack instance.
480      */
reInit()481     protected void reInit() {
482         if (stackLogger.isLoggingEnabled())
483             stackLogger.logDebug("Re-initializing !");
484 
485         // Array of message processors.
486         messageProcessors = new ArrayList<MessageProcessor>();
487         // Handle IO for this process.
488         this.ioHandler = new IOHandler(this);
489         // clientTransactions = new ConcurrentLinkedQueue();
490         // serverTransactions = new ConcurrentLinkedQueue();
491         pendingTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
492         clientTransactionTable = new ConcurrentHashMap<String, SIPClientTransaction>();
493         serverTransactionTable = new ConcurrentHashMap<String, SIPServerTransaction>();
494         retransmissionAlertTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
495         mergeTable = new ConcurrentHashMap<String, SIPServerTransaction>();
496         // Dialog dable.
497         this.dialogTable = new ConcurrentHashMap<String, SIPDialog>();
498         this.earlyDialogTable = new ConcurrentHashMap<String, SIPDialog>();
499         this.terminatedServerTransactionsPendingAck = new ConcurrentHashMap<String,SIPServerTransaction>();
500         this.forkedClientTransactionTable = new ConcurrentHashMap<String,SIPClientTransaction>();
501 
502         this.timer = new Timer();
503 
504         this.activeClientTransactionCount = new AtomicInteger(0);
505 
506     }
507 
508     /**
509      * Creates and binds, if necessary, a socket connected to the specified
510      * destination address and port and then returns its local address.
511      *
512      * @param dst the destination address that the socket would need to connect
513      *            to.
514      * @param dstPort the port number that the connection would be established
515      * with.
516      * @param localAddress the address that we would like to bind on
517      * (null for the "any" address).
518      * @param localPort the port that we'd like our socket to bind to (0 for a
519      * random port).
520      *
521      * @return the SocketAddress that this handler would use when connecting to
522      * the specified destination address and port.
523      *
524      * @throws IOException
525      */
obtainLocalAddress(InetAddress dst, int dstPort, InetAddress localAddress, int localPort)526     public SocketAddress obtainLocalAddress(InetAddress dst, int dstPort,
527                     InetAddress localAddress, int localPort)
528         throws IOException
529     {
530         return this.ioHandler.obtainLocalAddress(
531                         dst, dstPort, localAddress, localPort);
532 
533     }
534 
535     /**
536      * For debugging -- allows you to disable logging or enable logging selectively.
537      *
538      *
539      */
disableLogging()540     public void disableLogging() {
541         this.getStackLogger().disableLogging();
542     }
543 
544     /**
545      * Globally enable message logging ( for debugging)
546      *
547      */
enableLogging()548     public void enableLogging() {
549         this.getStackLogger().enableLogging();
550     }
551 
552     /**
553      * Print the dialog table.
554      *
555      */
printDialogTable()556     public void printDialogTable() {
557         if (isLoggingEnabled()) {
558             this.getStackLogger().logDebug("dialog table  = " + this.dialogTable);
559             System.out.println("dialog table = " + this.dialogTable);
560         }
561     }
562 
563     /**
564      * Retrieve a transaction from our table of transactions with pending retransmission alerts.
565      *
566      * @param dialogId
567      * @return -- the RetransmissionAlert enabled transaction corresponding to the given dialog
568      *         ID.
569      */
getRetransmissionAlertTransaction(String dialogId)570     public SIPServerTransaction getRetransmissionAlertTransaction(String dialogId) {
571         return (SIPServerTransaction) this.retransmissionAlertTransactions.get(dialogId);
572     }
573 
574     /**
575      * Return true if extension is supported.
576      *
577      * @return true if extension is supported and false otherwise.
578      */
isDialogCreated(String method)579     public static boolean isDialogCreated(String method) {
580     	return dialogCreatingMethods.contains(method);
581     }
582 
583     /**
584      * Add an extension method.
585      *
586      * @param extensionMethod -- extension method to support for dialog creation
587      */
addExtensionMethod(String extensionMethod)588     public void addExtensionMethod(String extensionMethod) {
589         if (extensionMethod.equals(Request.NOTIFY)) {
590             if (stackLogger.isLoggingEnabled())
591                 stackLogger.logDebug("NOTIFY Supported Natively");
592         } else {
593             dialogCreatingMethods.add(extensionMethod.trim().toUpperCase());
594         }
595     }
596 
597     /**
598      * Put a dialog into the dialog table.
599      *
600      * @param dialog -- dialog to put into the dialog table.
601      *
602      */
putDialog(SIPDialog dialog)603     public void putDialog(SIPDialog dialog) {
604         String dialogId = dialog.getDialogId();
605         if (dialogTable.containsKey(dialogId)) {
606             if (stackLogger.isLoggingEnabled()) {
607                 stackLogger.logDebug("putDialog: dialog already exists" + dialogId + " in table = "
608                         + dialogTable.get(dialogId));
609             }
610             return;
611         }
612         if (stackLogger.isLoggingEnabled()) {
613             stackLogger.logDebug("putDialog dialogId=" + dialogId + " dialog = " + dialog);
614         }
615         dialog.setStack(this);
616         if (stackLogger.isLoggingEnabled())
617             stackLogger.logStackTrace();
618         dialogTable.put(dialogId, dialog);
619 
620     }
621 
622     /**
623      * Create a dialog and add this transaction to it.
624      *
625      * @param transaction -- tx to add to the dialog.
626      * @return the newly created Dialog.
627      */
createDialog(SIPTransaction transaction)628     public SIPDialog createDialog(SIPTransaction transaction) {
629 
630         SIPDialog retval = null;
631 
632         if (transaction instanceof SIPClientTransaction) {
633             String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
634             if (this.earlyDialogTable.get(dialogId) != null) {
635                 SIPDialog dialog = this.earlyDialogTable.get(dialogId);
636                 if (dialog.getState() == null || dialog.getState() == DialogState.EARLY) {
637                     retval = dialog;
638                 } else {
639                     retval = new SIPDialog(transaction);
640                     this.earlyDialogTable.put(dialogId, retval);
641                 }
642             } else {
643                 retval = new SIPDialog(transaction);
644                 this.earlyDialogTable.put(dialogId, retval);
645             }
646         } else {
647             retval = new SIPDialog(transaction);
648         }
649 
650         return retval;
651 
652     }
653 
654     /**
655      * Create a Dialog given a client tx and response.
656      *
657      * @param transaction
658      * @param sipResponse
659      * @return
660      */
661 
createDialog(SIPClientTransaction transaction, SIPResponse sipResponse)662     public SIPDialog createDialog(SIPClientTransaction transaction, SIPResponse sipResponse) {
663         String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
664         SIPDialog retval = null;
665         if (this.earlyDialogTable.get(dialogId) != null) {
666             retval = this.earlyDialogTable.get(dialogId);
667             if (sipResponse.isFinalResponse()) {
668                 this.earlyDialogTable.remove(dialogId);
669             }
670 
671         } else {
672             retval = new SIPDialog(transaction, sipResponse);
673         }
674         return retval;
675 
676     }
677     /**
678      * Create a Dialog given a sip provider and response.
679      *
680      * @param sipProvider
681      * @param sipResponse
682      * @return
683      */
createDialog(SipProviderImpl sipProvider, SIPResponse sipResponse)684     public SIPDialog createDialog(SipProviderImpl sipProvider,
685 			SIPResponse sipResponse) {
686 		return new SIPDialog(sipProvider, sipResponse);
687 	}
688 
689     /**
690      * Remove the dialog from the dialog table.
691      *
692      * @param dialog -- dialog to remove.
693      */
removeDialog(SIPDialog dialog)694     public void removeDialog(SIPDialog dialog) {
695 
696         String id = dialog.getDialogId();
697 
698         String earlyId = dialog.getEarlyDialogId();
699 
700         if (earlyId != null) {
701             this.earlyDialogTable.remove(earlyId);
702             this.dialogTable.remove(earlyId);
703         }
704 
705         if (id != null) {
706 
707             // FHT: Remove dialog from table only if its associated dialog is the same as the one
708             // specified
709 
710             Object old = this.dialogTable.get(id);
711 
712             if (old == dialog) {
713                 this.dialogTable.remove(id);
714             }
715 
716             // We now deliver DTE even when the dialog is not originally present in the Dialog
717             // Table
718             // This happens before the dialog state is assigned.
719 
720             if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
721                 DialogTerminatedEvent event = new DialogTerminatedEvent(dialog.getSipProvider(),
722                         dialog);
723 
724                 // Provide notification to the listener that the dialog has
725                 // ended.
726                 dialog.getSipProvider().handleEvent(event, null);
727 
728             }
729 
730         } else if ( this.isDialogTerminatedEventDeliveredForNullDialog ) {
731             if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
732                 DialogTerminatedEvent event = new DialogTerminatedEvent(dialog.getSipProvider(),
733                         dialog);
734 
735                 // Provide notification to the listener that the dialog has
736                 // ended.
737                 dialog.getSipProvider().handleEvent(event, null);
738 
739             }
740         }
741 
742     }
743 
744     /**
745      * Return the dialog for a given dialog ID. If compatibility is enabled then we do not assume
746      * the presence of tags and hence need to add a flag to indicate whether this is a server or
747      * client transaction.
748      *
749      * @param dialogId is the dialog id to check.
750      */
751 
getDialog(String dialogId)752     public SIPDialog getDialog(String dialogId) {
753 
754         SIPDialog sipDialog = (SIPDialog) dialogTable.get(dialogId);
755         if (stackLogger.isLoggingEnabled()) {
756             stackLogger.logDebug("getDialog(" + dialogId + ") : returning " + sipDialog);
757         }
758         return sipDialog;
759 
760     }
761 
762     /**
763      * Remove the dialog given its dialog id. This is used for dialog id re-assignment only.
764      *
765      * @param dialogId is the dialog Id to remove.
766      */
removeDialog(String dialogId)767     public void removeDialog(String dialogId) {
768         if (stackLogger.isLoggingEnabled()) {
769             stackLogger.logWarning("Silently removing dialog from table");
770         }
771         dialogTable.remove(dialogId);
772     }
773 
774     /**
775      * Find a matching client SUBSCRIBE to the incoming notify. NOTIFY requests are matched to
776      * such SUBSCRIBE requests if they contain the same "Call-ID", a "To" header "tag" parameter
777      * which matches the "From" header "tag" parameter of the SUBSCRIBE, and the same "Event"
778      * header field. Rules for comparisons of the "Event" headers are described in section 7.2.1.
779      * If a matching NOTIFY request contains a "Subscription-State" of "active" or "pending", it
780      * creates a new subscription and a new dialog (unless they have already been created by a
781      * matching response, as described above).
782      *
783      * @param notifyMessage
784      * @return -- the matching ClientTransaction with semaphore aquired or null if no such client
785      *         transaction can be found.
786      */
findSubscribeTransaction(SIPRequest notifyMessage, ListeningPointImpl listeningPoint)787     public SIPClientTransaction findSubscribeTransaction(SIPRequest notifyMessage,
788             ListeningPointImpl listeningPoint) {
789         SIPClientTransaction retval = null;
790         try {
791             Iterator it = clientTransactionTable.values().iterator();
792             if (stackLogger.isLoggingEnabled())
793             	stackLogger.logDebug("ct table size = " + clientTransactionTable.size());
794             String thisToTag = notifyMessage.getTo().getTag();
795             if (thisToTag == null) {
796                 return retval;
797             }
798             Event eventHdr = (Event) notifyMessage.getHeader(EventHeader.NAME);
799             if (eventHdr == null) {
800                 if (stackLogger.isLoggingEnabled()) {
801                     stackLogger.logDebug("event Header is null -- returning null");
802                 }
803 
804                 return retval;
805             }
806             while (it.hasNext()) {
807                 SIPClientTransaction ct = (SIPClientTransaction) it.next();
808                 if (!ct.getMethod().equals(Request.SUBSCRIBE))
809                     continue;
810 
811                 // if ( sipProvider.getListeningPoint(transport) == null)
812                 String fromTag = ct.from.getTag();
813                 Event hisEvent = ct.event;
814                 // Event header is mandatory but some slopply clients
815                 // dont include it.
816                 if (hisEvent == null)
817                     continue;
818                 if (stackLogger.isLoggingEnabled()) {
819                     stackLogger.logDebug("ct.fromTag = " + fromTag);
820                     stackLogger.logDebug("thisToTag = " + thisToTag);
821                     stackLogger.logDebug("hisEvent = " + hisEvent);
822                     stackLogger.logDebug("eventHdr " + eventHdr);
823                 }
824 
825                 if (  fromTag.equalsIgnoreCase(thisToTag)
826                       && hisEvent != null
827                       && eventHdr.match(hisEvent)
828                       && notifyMessage.getCallId().getCallId().equalsIgnoreCase(
829                                 ct.callId.getCallId())) {
830                     if (ct.acquireSem())
831                         retval = ct;
832                     return retval;
833                 }
834             }
835 
836             return retval;
837         } finally {
838         	if (stackLogger.isLoggingEnabled())
839                 stackLogger.logDebug("findSubscribeTransaction : returning " + retval);
840 
841         }
842 
843     }
844 
845     /**
846      * Add entry to "Transaction Pending ACK" table.
847      *
848      * @param serverTransaction
849      */
addTransactionPendingAck(SIPServerTransaction serverTransaction)850     public void addTransactionPendingAck(SIPServerTransaction serverTransaction) {
851         String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
852         if ( branchId != null ) {
853             this.terminatedServerTransactionsPendingAck.put(branchId, serverTransaction);
854         }
855 
856     }
857 
858     /**
859      * Get entry in the server transaction pending ACK table corresponding to an ACK.
860      *
861      * @param ackMessage
862      * @return
863      */
findTransactionPendingAck(SIPRequest ackMessage)864     public SIPServerTransaction findTransactionPendingAck(SIPRequest ackMessage) {
865         return this.terminatedServerTransactionsPendingAck.get(ackMessage.getTopmostVia().getBranch());
866     }
867 
868     /**
869      * Remove entry from "Transaction Pending ACK" table.
870      *
871      * @param serverTransaction
872      * @return
873      */
874 
removeTransactionPendingAck(SIPServerTransaction serverTransaction)875     public boolean removeTransactionPendingAck(SIPServerTransaction serverTransaction) {
876         String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
877         if ( branchId != null && this.terminatedServerTransactionsPendingAck.containsKey(branchId) ) {
878             this.terminatedServerTransactionsPendingAck.remove(branchId);
879             return true;
880         } else {
881             return false;
882         }
883     }
884 
885     /**
886      * Check if this entry exists in the "Transaction Pending ACK" table.
887      *
888      * @param serverTransaction
889      * @return
890      */
isTransactionPendingAck(SIPServerTransaction serverTransaction)891     public boolean isTransactionPendingAck(SIPServerTransaction serverTransaction) {
892         String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
893         return this.terminatedServerTransactionsPendingAck.contains(branchId);
894     }
895 
896     /**
897      * Find the transaction corresponding to a given request.
898      *
899      * @param sipMessage request for which to retrieve the transaction.
900      *
901      * @param isServer search the server transaction table if true.
902      *
903      * @return the transaction object corresponding to the request or null if no such mapping
904      *         exists.
905      */
findTransaction(SIPMessage sipMessage, boolean isServer)906     public SIPTransaction findTransaction(SIPMessage sipMessage, boolean isServer) {
907         SIPTransaction retval = null;
908         try {
909             if (isServer) {
910                 Via via = sipMessage.getTopmostVia();
911                 if (via.getBranch() != null) {
912                     String key = sipMessage.getTransactionId();
913 
914                     retval = (SIPTransaction) serverTransactionTable.get(key);
915                     if (stackLogger.isLoggingEnabled())
916                         getStackLogger().logDebug(
917                                 "serverTx: looking for key " + key + " existing="
918                                 + serverTransactionTable);
919                     if (key.startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
920                         return retval;
921                     }
922 
923                 }
924                 // Need to scan the table for old style transactions (RFC 2543
925                 // style)
926                 Iterator<SIPServerTransaction> it = serverTransactionTable.values().iterator();
927                 while (it.hasNext()) {
928                     SIPServerTransaction sipServerTransaction = (SIPServerTransaction) it.next();
929                     if (sipServerTransaction.isMessagePartOfTransaction(sipMessage)) {
930                         retval = sipServerTransaction;
931                         return retval;
932                     }
933                 }
934 
935             } else {
936                 Via via = sipMessage.getTopmostVia();
937                 if (via.getBranch() != null) {
938                     String key = sipMessage.getTransactionId();
939                     if (stackLogger.isLoggingEnabled())
940                         getStackLogger().logDebug("clientTx: looking for key " + key);
941                     retval = (SIPTransaction) clientTransactionTable.get(key);
942                     if (key.startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
943                         return retval;
944                     }
945 
946                 }
947                 // Need to scan the table for old style transactions (RFC 2543
948                 // style). This is terribly slow but we need to do this
949                 // for backasswords compatibility.
950                 Iterator<SIPClientTransaction> it = clientTransactionTable.values().iterator();
951                 while (it.hasNext()) {
952                     SIPClientTransaction clientTransaction = (SIPClientTransaction) it.next();
953                     if (clientTransaction.isMessagePartOfTransaction(sipMessage)) {
954                         retval = clientTransaction;
955                         return retval;
956                     }
957                 }
958 
959             }
960         } finally {
961         	if ( this.getStackLogger().isLoggingEnabled()) {
962         	  this.getStackLogger().logDebug("findTransaction: returning  : " + retval);
963         	}
964         }
965         return retval;
966 
967     }
968 
969     /**
970      * Get the transaction to cancel. Search the server transaction table for a transaction that
971      * matches the given transaction.
972      */
findCancelTransaction(SIPRequest cancelRequest, boolean isServer)973     public SIPTransaction findCancelTransaction(SIPRequest cancelRequest, boolean isServer) {
974 
975         if (stackLogger.isLoggingEnabled()) {
976             stackLogger.logDebug("findCancelTransaction request= \n" + cancelRequest
977                     + "\nfindCancelRequest isServer=" + isServer);
978         }
979 
980         if (isServer) {
981             Iterator<SIPServerTransaction> li = this.serverTransactionTable.values().iterator();
982             while (li.hasNext()) {
983                 SIPTransaction transaction = (SIPTransaction) li.next();
984 
985                 SIPServerTransaction sipServerTransaction = (SIPServerTransaction) transaction;
986                 if (sipServerTransaction.doesCancelMatchTransaction(cancelRequest))
987                     return sipServerTransaction;
988             }
989 
990         } else {
991             Iterator<SIPClientTransaction> li = this.clientTransactionTable.values().iterator();
992             while (li.hasNext()) {
993                 SIPTransaction transaction = (SIPTransaction) li.next();
994 
995                 SIPClientTransaction sipClientTransaction = (SIPClientTransaction) transaction;
996                 if (sipClientTransaction.doesCancelMatchTransaction(cancelRequest))
997                     return sipClientTransaction;
998 
999             }
1000 
1001         }
1002         if (stackLogger.isLoggingEnabled())
1003             stackLogger.logDebug("Could not find transaction for cancel request");
1004         return null;
1005     }
1006 
1007     /**
1008      * Construcor for the stack. Registers the request and response factories for the stack.
1009      *
1010      * @param messageFactory User-implemented factory for processing messages.
1011      */
SIPTransactionStack(StackMessageFactory messageFactory)1012     protected SIPTransactionStack(StackMessageFactory messageFactory) {
1013         this();
1014         this.sipMessageFactory = messageFactory;
1015     }
1016 
1017     /**
1018      * Finds a pending server transaction. Since each request may be handled either statefully or
1019      * statelessly, we keep a map of pending transactions so that a duplicate transaction is not
1020      * created if a second request is recieved while the first one is being processed.
1021      *
1022      * @param requestReceived
1023      * @return -- the pending transaction or null if no such transaction exists.
1024      */
findPendingTransaction(SIPRequest requestReceived)1025     public SIPServerTransaction findPendingTransaction(SIPRequest requestReceived) {
1026         if (this.stackLogger.isLoggingEnabled()) {
1027             this.stackLogger.logDebug("looking for pending tx for :"
1028                     + requestReceived.getTransactionId());
1029         }
1030         return (SIPServerTransaction) pendingTransactions.get(requestReceived.getTransactionId());
1031 
1032     }
1033 
1034     /**
1035      * See if there is a pending transaction with the same Merge ID as the Merge ID obtained from
1036      * the SIP Request. The Merge table is for handling the following condition: If the request
1037      * has no tag in the To header field, the UAS core MUST check the request against ongoing
1038      * transactions. If the From tag, Call-ID, and CSeq exactly match those associated with an
1039      * ongoing transaction, but the request does not match that transaction (based on the matching
1040      * rules in Section 17.2.3), the UAS core SHOULD generate a 482 (Loop Detected) response and
1041      * pass it to the server transaction.
1042      */
findMergedTransaction(SIPRequest sipRequest)1043     public SIPServerTransaction findMergedTransaction(SIPRequest sipRequest) {
1044         if (! sipRequest.getMethod().equals(Request.INVITE)) {
1045             /*
1046              * Dont need to worry about request merging for Non-INVITE transactions.
1047              */
1048             return null;
1049         }
1050         String mergeId = sipRequest.getMergeId();
1051         SIPServerTransaction mergedTransaction = (SIPServerTransaction) this.mergeTable.get(mergeId);
1052         if (mergeId == null ) {
1053             return null;
1054         } else if (mergedTransaction != null && !mergedTransaction.isMessagePartOfTransaction(sipRequest) ) {
1055             return mergedTransaction;
1056         } else {
1057             /*
1058              * Check the server transactions that have resulted in dialogs.
1059              */
1060            for (Dialog dialog: this.dialogTable.values() ) {
1061                SIPDialog sipDialog = (SIPDialog) dialog ;
1062                if (sipDialog.getFirstTransaction()  != null &&
1063                    sipDialog.getFirstTransaction() instanceof ServerTransaction) {
1064                    SIPServerTransaction serverTransaction = ((SIPServerTransaction) sipDialog.getFirstTransaction());
1065                    SIPRequest transactionRequest = ((SIPServerTransaction) sipDialog.getFirstTransaction()).getOriginalRequest();
1066                    if ( (! serverTransaction.isMessagePartOfTransaction(sipRequest))
1067                            && sipRequest.getMergeId().equals(transactionRequest.getMergeId())) {
1068                            return (SIPServerTransaction) sipDialog.getFirstTransaction();
1069                    }
1070                }
1071            }
1072            return null;
1073         }
1074     }
1075 
1076     /**
1077      * Remove a pending Server transaction from the stack. This is called after the user code has
1078      * completed execution in the listener.
1079      *
1080      * @param tr -- pending transaction to remove.
1081      */
removePendingTransaction(SIPServerTransaction tr)1082     public void removePendingTransaction(SIPServerTransaction tr) {
1083         if (this.stackLogger.isLoggingEnabled()) {
1084             this.stackLogger.logDebug("removePendingTx: " + tr.getTransactionId());
1085         }
1086         this.pendingTransactions.remove(tr.getTransactionId());
1087 
1088     }
1089 
1090     /**
1091      * Remove a transaction from the merge table.
1092      *
1093      * @param tr -- the server transaction to remove from the merge table.
1094      *
1095      */
removeFromMergeTable(SIPServerTransaction tr)1096     public void removeFromMergeTable(SIPServerTransaction tr) {
1097         if (stackLogger.isLoggingEnabled()) {
1098             this.stackLogger.logDebug("Removing tx from merge table ");
1099         }
1100         String key = ((SIPRequest) tr.getRequest()).getMergeId();
1101         if (key != null) {
1102             this.mergeTable.remove(key);
1103         }
1104     }
1105 
1106     /**
1107      * Put this into the merge request table.
1108      *
1109      * @param sipTransaction -- transaction to put into the merge table.
1110      *
1111      */
putInMergeTable(SIPServerTransaction sipTransaction, SIPRequest sipRequest)1112     public void putInMergeTable(SIPServerTransaction sipTransaction, SIPRequest sipRequest) {
1113         String mergeKey = sipRequest.getMergeId();
1114         if (mergeKey != null) {
1115             this.mergeTable.put(mergeKey, sipTransaction);
1116         }
1117     }
1118 
1119     /**
1120      * Map a Server transaction (possibly sending out a 100 if the server tx is an INVITE). This
1121      * actually places it in the hash table and makes it known to the stack.
1122      *
1123      * @param transaction -- the server transaction to map.
1124      */
mapTransaction(SIPServerTransaction transaction)1125     public void mapTransaction(SIPServerTransaction transaction) {
1126         if (transaction.isMapped)
1127             return;
1128         addTransactionHash(transaction);
1129         // transaction.startTransactionTimer();
1130         transaction.isMapped = true;
1131     }
1132 
1133     /**
1134      * Handles a new SIP request. It finds a server transaction to handle this message. If none
1135      * exists, it creates a new transaction.
1136      *
1137      * @param requestReceived Request to handle.
1138      * @param requestMessageChannel Channel that received message.
1139      *
1140      * @return A server transaction.
1141      */
newSIPServerRequest(SIPRequest requestReceived, MessageChannel requestMessageChannel)1142     public ServerRequestInterface newSIPServerRequest(SIPRequest requestReceived,
1143             MessageChannel requestMessageChannel) {
1144         // Iterator through all server transactions
1145         Iterator<SIPServerTransaction> transactionIterator;
1146         // Next transaction in the set
1147         SIPServerTransaction nextTransaction;
1148         // Transaction to handle this request
1149         SIPServerTransaction currentTransaction;
1150 
1151         String key = requestReceived.getTransactionId();
1152 
1153         requestReceived.setMessageChannel(requestMessageChannel);
1154 
1155         currentTransaction = (SIPServerTransaction) serverTransactionTable.get(key);
1156 
1157         // Got to do this for bacasswards compatibility.
1158         if (currentTransaction == null
1159                 || !currentTransaction.isMessagePartOfTransaction(requestReceived)) {
1160 
1161             // Loop through all server transactions
1162             transactionIterator = serverTransactionTable.values().iterator();
1163             currentTransaction = null;
1164             if (!key.toLowerCase().startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
1165                 while (transactionIterator.hasNext() && currentTransaction == null) {
1166 
1167                     nextTransaction = (SIPServerTransaction) transactionIterator.next();
1168 
1169                     // If this transaction should handle this request,
1170                     if (nextTransaction.isMessagePartOfTransaction(requestReceived)) {
1171                         // Mark this transaction as the one
1172                         // to handle this message
1173                         currentTransaction = nextTransaction;
1174                     }
1175                 }
1176             }
1177 
1178             // If no transaction exists to handle this message
1179             if (currentTransaction == null) {
1180                 currentTransaction = findPendingTransaction(requestReceived);
1181                 if (currentTransaction != null) {
1182                     // Associate the tx with the received request.
1183                     requestReceived.setTransaction(currentTransaction);
1184                     if (currentTransaction != null && currentTransaction.acquireSem())
1185                         return currentTransaction;
1186                     else
1187                         return null;
1188 
1189                 }
1190                 // Creating a new server tx. May fail under heavy load.
1191                 currentTransaction = createServerTransaction(requestMessageChannel);
1192                 if (currentTransaction != null) {
1193                     // currentTransaction.setPassToListener();
1194                     currentTransaction.setOriginalRequest(requestReceived);
1195                     // Associate the tx with the received request.
1196                     requestReceived.setTransaction(currentTransaction);
1197                 }
1198 
1199             }
1200 
1201         }
1202 
1203         // Set ths transaction's encapsulated request
1204         // interface from the superclass
1205         if (stackLogger.isLoggingEnabled()) {
1206             stackLogger.logDebug("newSIPServerRequest( " + requestReceived.getMethod() + ":"
1207                     + requestReceived.getTopmostVia().getBranch() + "):" + currentTransaction);
1208         }
1209 
1210         if (currentTransaction != null)
1211             currentTransaction.setRequestInterface(sipMessageFactory.newSIPServerRequest(
1212                     requestReceived, currentTransaction));
1213 
1214         if (currentTransaction != null && currentTransaction.acquireSem()) {
1215             return currentTransaction;
1216         } else if (currentTransaction != null) {
1217             try {
1218                 /*
1219                  * Already processing a message for this transaction.
1220                  * SEND a trying ( message already being processed ).
1221                  */
1222                 if (currentTransaction.isMessagePartOfTransaction(requestReceived) &&
1223                     currentTransaction.getMethod().equals(requestReceived.getMethod())) {
1224                     SIPResponse trying = requestReceived.createResponse(Response.TRYING);
1225                     trying.removeContent();
1226                     currentTransaction.getMessageChannel().sendMessage(trying);
1227                 }
1228             } catch (Exception ex) {
1229             	if (isLoggingEnabled())
1230             		stackLogger.logError("Exception occured sending TRYING");
1231             }
1232             return null;
1233         } else {
1234             return null;
1235         }
1236     }
1237 
1238     /**
1239      * Handles a new SIP response. It finds a client transaction to handle this message. If none
1240      * exists, it sends the message directly to the superclass.
1241      *
1242      * @param responseReceived Response to handle.
1243      * @param responseMessageChannel Channel that received message.
1244      *
1245      * @return A client transaction.
1246      */
newSIPServerResponse(SIPResponse responseReceived, MessageChannel responseMessageChannel)1247     public ServerResponseInterface newSIPServerResponse(SIPResponse responseReceived,
1248             MessageChannel responseMessageChannel) {
1249 
1250         // Iterator through all client transactions
1251         Iterator<SIPClientTransaction> transactionIterator;
1252         // Next transaction in the set
1253         SIPClientTransaction nextTransaction;
1254         // Transaction to handle this request
1255         SIPClientTransaction currentTransaction;
1256 
1257         String key = responseReceived.getTransactionId();
1258 
1259         // Note that for RFC 3261 compliant operation, this lookup will
1260         // return a tx if one exists and hence no need to search through
1261         // the table.
1262         currentTransaction = (SIPClientTransaction) clientTransactionTable.get(key);
1263 
1264         if (currentTransaction == null
1265                 || (!currentTransaction.isMessagePartOfTransaction(responseReceived) && !key
1266                         .startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE))) {
1267             // Loop through all client transactions
1268 
1269             transactionIterator = clientTransactionTable.values().iterator();
1270             currentTransaction = null;
1271             while (transactionIterator.hasNext() && currentTransaction == null) {
1272 
1273                 nextTransaction = (SIPClientTransaction) transactionIterator.next();
1274 
1275                 // If this transaction should handle this request,
1276                 if (nextTransaction.isMessagePartOfTransaction(responseReceived)) {
1277 
1278                     // Mark this transaction as the one to
1279                     // handle this message
1280                     currentTransaction = nextTransaction;
1281 
1282                 }
1283 
1284             }
1285 
1286             // If no transaction exists to handle this message,
1287             if (currentTransaction == null) {
1288                 // JvB: Need to log before passing the response to the client
1289                 // app, it
1290                 // gets modified!
1291                 if (this.stackLogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
1292                     responseMessageChannel.logResponse(responseReceived, System
1293                             .currentTimeMillis(), "before processing");
1294                 }
1295 
1296                 // Pass the message directly to the TU
1297                 return sipMessageFactory.newSIPServerResponse(responseReceived,
1298                         responseMessageChannel);
1299 
1300             }
1301         }
1302 
1303         // Aquire the sem -- previous request may still be processing.
1304         boolean acquired = currentTransaction.acquireSem();
1305         // Set ths transaction's encapsulated response interface
1306         // from the superclass
1307         if (this.stackLogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
1308             currentTransaction.logResponse(responseReceived, System.currentTimeMillis(),
1309                     "before processing");
1310         }
1311 
1312         if (acquired) {
1313             ServerResponseInterface sri = sipMessageFactory.newSIPServerResponse(
1314                     responseReceived, currentTransaction);
1315             if (sri != null) {
1316                 currentTransaction.setResponseInterface(sri);
1317             } else {
1318                 if (this.stackLogger.isLoggingEnabled()) {
1319                     this.stackLogger.logDebug("returning null - serverResponseInterface is null!");
1320                 }
1321                 currentTransaction.releaseSem();
1322                 return null;
1323             }
1324         } else {
1325         	if (stackLogger.isLoggingEnabled())
1326         		this.stackLogger.logDebug("Could not aquire semaphore !!");
1327         }
1328 
1329         if (acquired)
1330             return currentTransaction;
1331         else
1332             return null;
1333 
1334     }
1335 
1336     /**
1337      * Creates a client transaction to handle a new request. Gets the real message channel from
1338      * the superclass, and then creates a new client transaction wrapped around this channel.
1339      *
1340      * @param nextHop Hop to create a channel to contact.
1341      */
createMessageChannel(SIPRequest request, MessageProcessor mp, Hop nextHop)1342     public MessageChannel createMessageChannel(SIPRequest request, MessageProcessor mp,
1343             Hop nextHop) throws IOException {
1344         // New client transaction to return
1345         SIPTransaction returnChannel;
1346 
1347         // Create a new client transaction around the
1348         // superclass' message channel
1349         // Create the host/port of the target hop
1350         Host targetHost = new Host();
1351         targetHost.setHostname(nextHop.getHost());
1352         HostPort targetHostPort = new HostPort();
1353         targetHostPort.setHost(targetHost);
1354         targetHostPort.setPort(nextHop.getPort());
1355         MessageChannel mc = mp.createMessageChannel(targetHostPort);
1356 
1357         // Superclass will return null if no message processor
1358         // available for the transport.
1359         if (mc == null)
1360             return null;
1361 
1362         returnChannel = createClientTransaction(request, mc);
1363 
1364         ((SIPClientTransaction) returnChannel).setViaPort(nextHop.getPort());
1365         ((SIPClientTransaction) returnChannel).setViaHost(nextHop.getHost());
1366         addTransactionHash(returnChannel);
1367         // clientTransactionTable.put(returnChannel.getTransactionId(),
1368         // returnChannel);
1369         // Add the transaction timer for the state machine.
1370         // returnChannel.startTransactionTimer();
1371         return returnChannel;
1372 
1373     }
1374 
1375     /**
1376      * Creates a client transaction that encapsulates a MessageChannel. Useful for implementations
1377      * that want to subclass the standard
1378      *
1379      * @param encapsulatedMessageChannel Message channel of the transport layer.
1380      */
createClientTransaction(SIPRequest sipRequest, MessageChannel encapsulatedMessageChannel)1381     public SIPClientTransaction createClientTransaction(SIPRequest sipRequest,
1382             MessageChannel encapsulatedMessageChannel) {
1383         SIPClientTransaction ct = new SIPClientTransaction(this, encapsulatedMessageChannel);
1384         ct.setOriginalRequest(sipRequest);
1385         return ct;
1386     }
1387 
1388     /**
1389      * Creates a server transaction that encapsulates a MessageChannel. Useful for implementations
1390      * that want to subclass the standard
1391      *
1392      * @param encapsulatedMessageChannel Message channel of the transport layer.
1393      */
createServerTransaction(MessageChannel encapsulatedMessageChannel)1394     public SIPServerTransaction createServerTransaction(MessageChannel encapsulatedMessageChannel) {
1395     	// Issue 256 : be consistent with createClientTransaction, if unlimitedServerTransactionTableSize is true,
1396     	// a new Server Transaction is created no matter what
1397         if (unlimitedServerTransactionTableSize) {
1398             return new SIPServerTransaction(this, encapsulatedMessageChannel);
1399         } else {
1400             float threshold = ((float) (serverTransactionTable.size() - serverTransactionTableLowaterMark))
1401                     / ((float) (serverTransactionTableHighwaterMark - serverTransactionTableLowaterMark));
1402             boolean decision = Math.random() > 1.0 - threshold;
1403             if (decision) {
1404                 return null;
1405             } else {
1406                 return new SIPServerTransaction(this, encapsulatedMessageChannel);
1407             }
1408 
1409         }
1410 
1411     }
1412 
1413     /**
1414      * Get the size of the client transaction table.
1415      *
1416      * @return -- size of the ct table.
1417      */
getClientTransactionTableSize()1418     public int getClientTransactionTableSize() {
1419         return this.clientTransactionTable.size();
1420     }
1421 
1422     /**
1423      * Get the size of the server transaction table.
1424      *
1425      * @return -- size of the server table.
1426      */
getServerTransactionTableSize()1427     public int getServerTransactionTableSize() {
1428         return this.serverTransactionTable.size();
1429     }
1430 
1431     /**
1432      * Add a new client transaction to the set of existing transactions. Add it to the top of the
1433      * list so an incoming response has less work to do in order to find the transaction.
1434      *
1435      * @param clientTransaction -- client transaction to add to the set.
1436      */
addTransaction(SIPClientTransaction clientTransaction)1437     public void addTransaction(SIPClientTransaction clientTransaction) {
1438         if (stackLogger.isLoggingEnabled())
1439             stackLogger.logDebug("added transaction " + clientTransaction);
1440         addTransactionHash(clientTransaction);
1441 
1442     }
1443 
1444     /**
1445      * Remove transaction. This actually gets the tx out of the search structures which the stack
1446      * keeps around. When the tx
1447      */
removeTransaction(SIPTransaction sipTransaction)1448     public void removeTransaction(SIPTransaction sipTransaction) {
1449         if (stackLogger.isLoggingEnabled()) {
1450             stackLogger.logDebug("Removing Transaction = " + sipTransaction.getTransactionId()
1451                     + " transaction = " + sipTransaction);
1452         }
1453         if (sipTransaction instanceof SIPServerTransaction) {
1454             if (stackLogger.isLoggingEnabled())
1455                 stackLogger.logStackTrace();
1456             String key = sipTransaction.getTransactionId();
1457             Object removed = serverTransactionTable.remove(key);
1458             String method = sipTransaction.getMethod();
1459             this.removePendingTransaction((SIPServerTransaction) sipTransaction);
1460             this.removeTransactionPendingAck((SIPServerTransaction) sipTransaction);
1461             if (method.equalsIgnoreCase(Request.INVITE)) {
1462                 this.removeFromMergeTable((SIPServerTransaction) sipTransaction);
1463             }
1464             // Send a notification to the listener.
1465             SipProviderImpl sipProvider = (SipProviderImpl) sipTransaction.getSipProvider();
1466             if (removed != null && sipTransaction.testAndSetTransactionTerminatedEvent()) {
1467                 TransactionTerminatedEvent event = new TransactionTerminatedEvent(sipProvider,
1468                         (ServerTransaction) sipTransaction);
1469 
1470                 sipProvider.handleEvent(event, sipTransaction);
1471 
1472             }
1473         } else {
1474 
1475             String key = sipTransaction.getTransactionId();
1476             Object removed = clientTransactionTable.remove(key);
1477 
1478             if (stackLogger.isLoggingEnabled()) {
1479                 stackLogger.logDebug("REMOVED client tx " + removed + " KEY = " + key);
1480                 if ( removed != null ) {
1481                    SIPClientTransaction clientTx = (SIPClientTransaction)removed;
1482                    if ( clientTx.getMethod().equals(Request.INVITE) && this.maxForkTime != 0 ) {
1483                        RemoveForkedTransactionTimerTask ttask = new RemoveForkedTransactionTimerTask(clientTx);
1484                        this.timer.schedule(ttask, this.maxForkTime * 1000);
1485                    }
1486                 }
1487             }
1488 
1489             // Send a notification to the listener.
1490             if (removed != null && sipTransaction.testAndSetTransactionTerminatedEvent()) {
1491                 SipProviderImpl sipProvider = (SipProviderImpl) sipTransaction.getSipProvider();
1492                 TransactionTerminatedEvent event = new TransactionTerminatedEvent(sipProvider,
1493                         (ClientTransaction) sipTransaction);
1494 
1495                 sipProvider.handleEvent(event, sipTransaction);
1496             }
1497 
1498         }
1499     }
1500 
1501     /**
1502      * Add a new server transaction to the set of existing transactions. Add it to the top of the
1503      * list so an incoming ack has less work to do in order to find the transaction.
1504      *
1505      * @param serverTransaction -- server transaction to add to the set.
1506      */
addTransaction(SIPServerTransaction serverTransaction)1507     public void addTransaction(SIPServerTransaction serverTransaction) throws IOException {
1508         if (stackLogger.isLoggingEnabled())
1509             stackLogger.logDebug("added transaction " + serverTransaction);
1510         serverTransaction.map();
1511 
1512         addTransactionHash(serverTransaction);
1513 
1514     }
1515 
1516     /**
1517      * Hash table for quick lookup of transactions. Here we wait for room if needed.
1518      */
addTransactionHash(SIPTransaction sipTransaction)1519     private void addTransactionHash(SIPTransaction sipTransaction) {
1520         SIPRequest sipRequest = sipTransaction.getOriginalRequest();
1521         if (sipTransaction instanceof SIPClientTransaction) {
1522             if (!this.unlimitedClientTransactionTableSize) {
1523                 if (this.activeClientTransactionCount.get() > clientTransactionTableHiwaterMark) {
1524                     try {
1525                         synchronized (this.clientTransactionTable) {
1526                             this.clientTransactionTable.wait();
1527                             this.activeClientTransactionCount.incrementAndGet();
1528                         }
1529 
1530                     } catch (Exception ex) {
1531                         if (stackLogger.isLoggingEnabled()) {
1532                             stackLogger.logError("Exception occured while waiting for room", ex);
1533                         }
1534 
1535                     }
1536                 }
1537             } else {
1538                 this.activeClientTransactionCount.incrementAndGet();
1539             }
1540             String key = sipRequest.getTransactionId();
1541             clientTransactionTable.put(key, (SIPClientTransaction) sipTransaction);
1542 
1543             if (stackLogger.isLoggingEnabled()) {
1544                 stackLogger.logDebug(" putTransactionHash : " + " key = " + key);
1545             }
1546         } else {
1547             String key = sipRequest.getTransactionId();
1548 
1549             if (stackLogger.isLoggingEnabled()) {
1550                 stackLogger.logDebug(" putTransactionHash : " + " key = " + key);
1551             }
1552             serverTransactionTable.put(key, (SIPServerTransaction) sipTransaction);
1553 
1554         }
1555 
1556     }
1557 
1558     /**
1559      * This method is called when a client tx transitions to the Completed or Terminated state.
1560      *
1561      */
decrementActiveClientTransactionCount()1562     protected void decrementActiveClientTransactionCount() {
1563 
1564         if (this.activeClientTransactionCount.decrementAndGet() <= this.clientTransactionTableLowaterMark
1565                 && !this.unlimitedClientTransactionTableSize) {
1566             synchronized (this.clientTransactionTable) {
1567 
1568                 clientTransactionTable.notify();
1569 
1570             }
1571         }
1572     }
1573 
1574     /**
1575      * Remove the transaction from transaction hash.
1576      */
removeTransactionHash(SIPTransaction sipTransaction)1577     protected void removeTransactionHash(SIPTransaction sipTransaction) {
1578         SIPRequest sipRequest = sipTransaction.getOriginalRequest();
1579         if (sipRequest == null)
1580             return;
1581         if (sipTransaction instanceof SIPClientTransaction) {
1582             String key = sipTransaction.getTransactionId();
1583             if (stackLogger.isLoggingEnabled()) {
1584                 stackLogger.logStackTrace();
1585                 stackLogger.logDebug("removing client Tx : " + key);
1586             }
1587             clientTransactionTable.remove(key);
1588 
1589         } else if (sipTransaction instanceof SIPServerTransaction) {
1590             String key = sipTransaction.getTransactionId();
1591             serverTransactionTable.remove(key);
1592             if (stackLogger.isLoggingEnabled()) {
1593                 stackLogger.logDebug("removing server Tx : " + key);
1594             }
1595         }
1596     }
1597 
1598     /**
1599      * Invoked when an error has ocurred with a transaction.
1600      *
1601      * @param transactionErrorEvent Error event.
1602      */
transactionErrorEvent(SIPTransactionErrorEvent transactionErrorEvent)1603     public synchronized void transactionErrorEvent(SIPTransactionErrorEvent transactionErrorEvent) {
1604         SIPTransaction transaction = (SIPTransaction) transactionErrorEvent.getSource();
1605 
1606         if (transactionErrorEvent.getErrorID() == SIPTransactionErrorEvent.TRANSPORT_ERROR) {
1607             // Kill scanning of this transaction.
1608             transaction.setState(SIPTransaction.TERMINATED_STATE);
1609             if (transaction instanceof SIPServerTransaction) {
1610                 // let the reaper get him
1611                 ((SIPServerTransaction) transaction).collectionTime = 0;
1612             }
1613             transaction.disableTimeoutTimer();
1614             transaction.disableRetransmissionTimer();
1615             // Send a IO Exception to the Listener.
1616         }
1617     }
1618 
1619     /*
1620      * (non-Javadoc)
1621      * @see gov.nist.javax.sip.stack.SIPDialogEventListener#dialogErrorEvent(gov.nist.javax.sip.stack.SIPDialogErrorEvent)
1622      */
dialogErrorEvent(SIPDialogErrorEvent dialogErrorEvent)1623     public synchronized void dialogErrorEvent(SIPDialogErrorEvent dialogErrorEvent) {
1624         SIPDialog sipDialog = (SIPDialog) dialogErrorEvent.getSource();
1625         SipListener sipListener = ((SipStackImpl)this).getSipListener();
1626         // if the app is not implementing the SipListenerExt interface we delete the dialog to avoid leaks
1627         if(sipDialog != null && !(sipListener instanceof SipListenerExt)) {
1628         	sipDialog.delete();
1629         }
1630     }
1631 
1632     /**
1633      * Stop stack. Clear all the timer stuff. Make the stack close all accept connections and
1634      * return. This is useful if you want to start/stop the stack several times from your
1635      * application. Caution : use of this function could cause peculiar bugs as messages are
1636      * prcessed asynchronously by the stack.
1637      */
stopStack()1638     public void stopStack() {
1639         // Prevent NPE on two concurrent stops
1640         if (this.timer != null)
1641             this.timer.cancel();
1642 
1643         // JvB: set it to null, SIPDialog tries to schedule things after stop
1644         timer = null;
1645         this.pendingTransactions.clear();
1646         this.toExit = true;
1647         synchronized (this) {
1648             this.notifyAll();
1649         }
1650         synchronized (this.clientTransactionTable) {
1651             clientTransactionTable.notifyAll();
1652         }
1653 
1654         synchronized (this.messageProcessors) {
1655             // Threads must periodically check this flag.
1656             MessageProcessor[] processorList;
1657             processorList = getMessageProcessors();
1658             for (int processorIndex = 0; processorIndex < processorList.length; processorIndex++) {
1659                 removeMessageProcessor(processorList[processorIndex]);
1660             }
1661             this.ioHandler.closeAll();
1662             // Let the processing complete.
1663 
1664         }
1665         try {
1666 
1667             Thread.sleep(1000);
1668 
1669         } catch (InterruptedException ex) {
1670         }
1671         this.clientTransactionTable.clear();
1672         this.serverTransactionTable.clear();
1673 
1674         this.dialogTable.clear();
1675         this.serverLogger.closeLogFile();
1676 
1677     }
1678 
1679     /**
1680      * Put a transaction in the pending transaction list. This is to avoid a race condition when a
1681      * duplicate may arrive when the application is deciding whether to create a transaction or
1682      * not.
1683      */
putPendingTransaction(SIPServerTransaction tr)1684     public void putPendingTransaction(SIPServerTransaction tr) {
1685         if (stackLogger.isLoggingEnabled())
1686             stackLogger.logDebug("putPendingTransaction: " + tr);
1687 
1688         this.pendingTransactions.put(tr.getTransactionId(), tr);
1689 
1690     }
1691 
1692     /**
1693      * Return the network layer (i.e. the interface for socket creation or the socket factory for
1694      * the stack).
1695      *
1696      * @return -- the registered Network Layer.
1697      */
getNetworkLayer()1698     public NetworkLayer getNetworkLayer() {
1699         if (networkLayer == null) {
1700             return DefaultNetworkLayer.SINGLETON;
1701         } else {
1702             return networkLayer;
1703         }
1704     }
1705 
1706     /**
1707      * Return true if logging is enabled for this stack.
1708      *
1709      * @return true if logging is enabled for this stack instance.
1710      */
isLoggingEnabled()1711     public boolean isLoggingEnabled() {
1712         return this.stackLogger == null ? false : this.stackLogger.isLoggingEnabled();
1713     }
1714 
1715     /**
1716      * Get the logger.
1717      *
1718      * @return --the logger for the sip stack. Each stack has its own logger instance.
1719      */
getStackLogger()1720     public StackLogger getStackLogger() {
1721         return this.stackLogger;
1722     }
1723 
1724     /**
1725      * Server log is the place where we log messages for the signaling trace viewer.
1726      *
1727      * @return -- the log file where messages are logged for viewing by the trace viewer.
1728      */
getServerLogger()1729     public ServerLogger getServerLogger() {
1730         return this.serverLogger;
1731     }
1732 
1733     /**
1734      * Maximum size of a single TCP message. Limiting the size of a single TCP message prevents
1735      * flooding attacks.
1736      *
1737      * @return the size of a single TCP message.
1738      */
getMaxMessageSize()1739     public int getMaxMessageSize() {
1740         return this.maxMessageSize;
1741     }
1742 
1743     /**
1744      * Set the flag that instructs the stack to only start a single thread for sequentially
1745      * processing incoming udp messages (thus serializing the processing). Same as setting thread
1746      * pool size to 1.
1747      */
setSingleThreaded()1748     public void setSingleThreaded() {
1749         this.threadPoolSize = 1;
1750     }
1751 
1752     /**
1753      * Set the thread pool size for processing incoming UDP messages. Limit the total number of
1754      * threads for processing udp messages.
1755      *
1756      * @param size -- the thread pool size.
1757      *
1758      */
setThreadPoolSize(int size)1759     public void setThreadPoolSize(int size) {
1760         this.threadPoolSize = size;
1761     }
1762 
1763     /**
1764      * Set the max # of simultaneously handled TCP connections.
1765      *
1766      * @param nconnections -- the number of connections to handle.
1767      */
setMaxConnections(int nconnections)1768     public void setMaxConnections(int nconnections) {
1769         this.maxConnections = nconnections;
1770     }
1771 
1772     /**
1773      * Get the default route string.
1774      *
1775      * @param sipRequest is the request for which we want to compute the next hop.
1776      * @throws SipException
1777      */
getNextHop(SIPRequest sipRequest)1778     public Hop getNextHop(SIPRequest sipRequest) throws SipException {
1779         if (this.useRouterForAll) {
1780             // Use custom router to route all messages.
1781             if (router != null)
1782                 return router.getNextHop(sipRequest);
1783             else
1784                 return null;
1785         } else {
1786             // Also non-SIP request containing Route headers goes to the default
1787             // router
1788             if (sipRequest.getRequestURI().isSipURI() || sipRequest.getRouteHeaders() != null) {
1789                 return defaultRouter.getNextHop(sipRequest);
1790             } else if (router != null) {
1791                 return router.getNextHop(sipRequest);
1792             } else
1793                 return null;
1794         }
1795     }
1796 
1797     /**
1798      * Set the descriptive name of the stack.
1799      *
1800      * @param stackName -- descriptive name of the stack.
1801      */
setStackName(String stackName)1802     public void setStackName(String stackName) {
1803         this.stackName = stackName;
1804     }
1805 
1806 
1807 
1808     /**
1809      * Set my address.
1810      *
1811      * @param stackAddress -- A string containing the stack address.
1812      */
setHostAddress(String stackAddress)1813     protected void setHostAddress(String stackAddress) throws UnknownHostException {
1814         if (stackAddress.indexOf(':') != stackAddress.lastIndexOf(':')
1815                 && stackAddress.trim().charAt(0) != '[')
1816             this.stackAddress = '[' + stackAddress + ']';
1817         else
1818             this.stackAddress = stackAddress;
1819         this.stackInetAddress = InetAddress.getByName(stackAddress);
1820     }
1821 
1822     /**
1823      * Get my address.
1824      *
1825      * @return hostAddress - my host address or null if no host address is defined.
1826      * @deprecated
1827      */
getHostAddress()1828     public String getHostAddress() {
1829 
1830         // JvB: for 1.2 this may return null...
1831         return this.stackAddress;
1832     }
1833 
1834     /**
1835      * Set the router algorithm. This is meant for routing messages out of dialog or for non-sip
1836      * uri's.
1837      *
1838      * @param router A class that implements the Router interface.
1839      */
setRouter(Router router)1840     protected void setRouter(Router router) {
1841         this.router = router;
1842     }
1843 
1844     /**
1845      * Get the router algorithm.
1846      *
1847      * @return Router router
1848      */
getRouter(SIPRequest request)1849     public Router getRouter(SIPRequest request) {
1850         if (request.getRequestLine() == null) {
1851             return this.defaultRouter;
1852         } else if (this.useRouterForAll) {
1853             return this.router;
1854         } else {
1855             if (request.getRequestURI().getScheme().equals("sip")
1856                     || request.getRequestURI().getScheme().equals("sips")) {
1857                 return this.defaultRouter;
1858             } else {
1859                 if (this.router != null)
1860                     return this.router;
1861                 else
1862                     return defaultRouter;
1863             }
1864         }
1865     }
1866 
1867     /*
1868      * (non-Javadoc)
1869      *
1870      * @see javax.sip.SipStack#getRouter()
1871      */
getRouter()1872     public Router getRouter() {
1873         return this.router;
1874     }
1875 
1876     /**
1877      * return the status of the toExit flag.
1878      *
1879      * @return true if the stack object is alive and false otherwise.
1880      */
isAlive()1881     public boolean isAlive() {
1882         return !toExit;
1883     }
1884 
1885     /**
1886      * Adds a new MessageProcessor to the list of running processors for this SIPStack and starts
1887      * it. You can use this method for dynamic stack configuration.
1888      */
addMessageProcessor(MessageProcessor newMessageProcessor)1889     protected void addMessageProcessor(MessageProcessor newMessageProcessor) throws IOException {
1890         synchronized (messageProcessors) {
1891             // Suggested changes by Jeyashankher, jai@lucent.com
1892             // newMessageProcessor.start() can fail
1893             // because a local port is not available
1894             // This throws an IOException.
1895             // We should not add the message processor to the
1896             // local list of processors unless the start()
1897             // call is successful.
1898             // newMessageProcessor.start();
1899             messageProcessors.add(newMessageProcessor);
1900 
1901         }
1902     }
1903 
1904     /**
1905      * Removes a MessageProcessor from this SIPStack.
1906      *
1907      * @param oldMessageProcessor
1908      */
removeMessageProcessor(MessageProcessor oldMessageProcessor)1909     protected void removeMessageProcessor(MessageProcessor oldMessageProcessor) {
1910         synchronized (messageProcessors) {
1911             if (messageProcessors.remove(oldMessageProcessor)) {
1912                 oldMessageProcessor.stop();
1913             }
1914         }
1915     }
1916 
1917     /**
1918      * Gets an array of running MessageProcessors on this SIPStack. Acknowledgement: Jeff Keyser
1919      * suggested that applications should have access to the running message processors and
1920      * contributed this code.
1921      *
1922      * @return an array of running message processors.
1923      */
getMessageProcessors()1924     protected MessageProcessor[] getMessageProcessors() {
1925         synchronized (messageProcessors) {
1926             return (MessageProcessor[]) messageProcessors.toArray(new MessageProcessor[0]);
1927         }
1928     }
1929 
1930     /**
1931      * Creates the equivalent of a JAIN listening point and attaches to the stack.
1932      *
1933      * @param ipAddress -- ip address for the listening point.
1934      * @param port -- port for the listening point.
1935      * @param transport -- transport for the listening point.
1936      */
createMessageProcessor(InetAddress ipAddress, int port, String transport)1937     protected MessageProcessor createMessageProcessor(InetAddress ipAddress, int port,
1938             String transport) throws java.io.IOException {
1939         if (transport.equalsIgnoreCase("udp")) {
1940             UDPMessageProcessor udpMessageProcessor = new UDPMessageProcessor(ipAddress, this,
1941                     port);
1942             this.addMessageProcessor(udpMessageProcessor);
1943             this.udpFlag = true;
1944             return udpMessageProcessor;
1945         } else if (transport.equalsIgnoreCase("tcp")) {
1946             TCPMessageProcessor tcpMessageProcessor = new TCPMessageProcessor(ipAddress, this,
1947                     port);
1948             this.addMessageProcessor(tcpMessageProcessor);
1949             // this.tcpFlag = true;
1950             return tcpMessageProcessor;
1951         } else if (transport.equalsIgnoreCase("tls")) {
1952             TLSMessageProcessor tlsMessageProcessor = new TLSMessageProcessor(ipAddress, this,
1953                     port);
1954             this.addMessageProcessor(tlsMessageProcessor);
1955             // this.tlsFlag = true;
1956             return tlsMessageProcessor;
1957         } else if (transport.equalsIgnoreCase("sctp")) {
1958 
1959         	// Need Java 7 for this, so these classes are packaged in a separate jar
1960         	// Try to load it indirectly, if fails report an error
1961         	try {
1962 				Class<?> mpc = ClassLoader.getSystemClassLoader().loadClass( "gov.nist.javax.sip.stack.sctp.SCTPMessageProcessor" );
1963 				MessageProcessor mp = (MessageProcessor) mpc.newInstance();
1964 				mp.initialize( ipAddress, port, this );
1965 				this.addMessageProcessor(mp);
1966 				return mp;
1967 			} catch (ClassNotFoundException e) {
1968 				throw new IllegalArgumentException("SCTP not supported (needs Java 7 and SCTP jar in classpath)");
1969 			} catch ( InstantiationException ie ) {
1970 				throw new IllegalArgumentException("Error initializing SCTP", ie);
1971 			} catch ( IllegalAccessException ie ) {
1972 				throw new IllegalArgumentException("Error initializing SCTP", ie);
1973 			}
1974         } else {
1975             throw new IllegalArgumentException("bad transport");
1976         }
1977 
1978     }
1979 
1980     /**
1981      * Set the message factory.
1982      *
1983      * @param messageFactory -- messageFactory to set.
1984      */
setMessageFactory(StackMessageFactory messageFactory)1985     protected void setMessageFactory(StackMessageFactory messageFactory) {
1986         this.sipMessageFactory = messageFactory;
1987     }
1988 
1989     /**
1990      * Creates a new MessageChannel for a given Hop.
1991      *
1992      * @param sourceIpAddress - Ip address of the source of this message.
1993      *
1994      * @param sourcePort - source port of the message channel to be created.
1995      *
1996      * @param nextHop Hop to create a MessageChannel to.
1997      *
1998      * @return A MessageChannel to the specified Hop, or null if no MessageProcessors support
1999      *         contacting that Hop.
2000      *
2001      * @throws UnknownHostException If the host in the Hop doesn't exist.
2002      */
createRawMessageChannel(String sourceIpAddress, int sourcePort, Hop nextHop)2003     public MessageChannel createRawMessageChannel(String sourceIpAddress, int sourcePort,
2004             Hop nextHop) throws UnknownHostException {
2005         Host targetHost;
2006         HostPort targetHostPort;
2007         Iterator processorIterator;
2008         MessageProcessor nextProcessor;
2009         MessageChannel newChannel;
2010 
2011         // Create the host/port of the target hop
2012         targetHost = new Host();
2013         targetHost.setHostname(nextHop.getHost());
2014         targetHostPort = new HostPort();
2015         targetHostPort.setHost(targetHost);
2016         targetHostPort.setPort(nextHop.getPort());
2017 
2018         // Search each processor for the correct transport
2019         newChannel = null;
2020         processorIterator = messageProcessors.iterator();
2021         while (processorIterator.hasNext() && newChannel == null) {
2022             nextProcessor = (MessageProcessor) processorIterator.next();
2023             // If a processor that supports the correct
2024             // transport is found,
2025             if (nextHop.getTransport().equalsIgnoreCase(nextProcessor.getTransport())
2026                     && sourceIpAddress.equals(nextProcessor.getIpAddress().getHostAddress())
2027                     && sourcePort == nextProcessor.getPort()) {
2028                 try {
2029                     // Create a channel to the target
2030                     // host/port
2031                     newChannel = nextProcessor.createMessageChannel(targetHostPort);
2032                 } catch (UnknownHostException ex) {
2033                     if (stackLogger.isLoggingEnabled())
2034                         stackLogger.logException(ex);
2035                     throw ex;
2036                 } catch (IOException e) {
2037                     if (stackLogger.isLoggingEnabled())
2038                         stackLogger.logException(e);
2039                     // Ignore channel creation error -
2040                     // try next processor
2041                 }
2042             }
2043         }
2044         // Return the newly-created channel
2045         return newChannel;
2046     }
2047 
2048     /**
2049      * Return true if a given event can result in a forked subscription. The stack is configured
2050      * with a set of event names that can result in forked subscriptions.
2051      *
2052      * @param ename -- event name to check.
2053      *
2054      */
isEventForked(String ename)2055     public boolean isEventForked(String ename) {
2056         if (stackLogger.isLoggingEnabled()) {
2057             stackLogger.logDebug("isEventForked: " + ename + " returning "
2058                     + this.forkedEvents.contains(ename));
2059         }
2060         return this.forkedEvents.contains(ename);
2061     }
2062 
2063     /**
2064      * get the address resolver interface.
2065      *
2066      * @return -- the registered address resolver.
2067      */
getAddressResolver()2068     public AddressResolver getAddressResolver() {
2069         return this.addressResolver;
2070     }
2071 
2072     /**
2073      * Set the address resolution interface
2074      *
2075      * @param addressResolver -- the address resolver to set.
2076      */
setAddressResolver(AddressResolver addressResolver)2077     public void setAddressResolver(AddressResolver addressResolver) {
2078         this.addressResolver = addressResolver;
2079     }
2080 
2081     /**
2082      * Set the logger factory.
2083      *
2084      * @param logRecordFactory -- the log record factory to set.
2085      */
setLogRecordFactory(LogRecordFactory logRecordFactory)2086     public void setLogRecordFactory(LogRecordFactory logRecordFactory) {
2087         this.logRecordFactory = logRecordFactory;
2088     }
2089 
2090     /**
2091      * get the thread auditor object
2092      *
2093      * @return -- the thread auditor of the stack
2094      */
getThreadAuditor()2095     public ThreadAuditor getThreadAuditor() {
2096         return this.threadAuditor;
2097     }
2098 
2099     // /
2100     // / Stack Audit methods
2101     // /
2102 
2103     /**
2104      * Audits the SIP Stack for leaks
2105      *
2106      * @return Audit report, null if no leaks were found
2107      */
auditStack(Set activeCallIDs, long leakedDialogTimer, long leakedTransactionTimer)2108     public String auditStack(Set activeCallIDs, long leakedDialogTimer,
2109             long leakedTransactionTimer) {
2110         String auditReport = null;
2111         String leakedDialogs = auditDialogs(activeCallIDs, leakedDialogTimer);
2112         String leakedServerTransactions = auditTransactions(serverTransactionTable,
2113                 leakedTransactionTimer);
2114         String leakedClientTransactions = auditTransactions(clientTransactionTable,
2115                 leakedTransactionTimer);
2116         if (leakedDialogs != null || leakedServerTransactions != null
2117                 || leakedClientTransactions != null) {
2118             auditReport = "SIP Stack Audit:\n" + (leakedDialogs != null ? leakedDialogs : "")
2119                     + (leakedServerTransactions != null ? leakedServerTransactions : "")
2120                     + (leakedClientTransactions != null ? leakedClientTransactions : "");
2121         }
2122         return auditReport;
2123     }
2124 
2125     /**
2126      * Audits SIP dialogs for leaks - Compares the dialogs in the dialogTable with a list of Call
2127      * IDs passed by the application. - Dialogs that are not known by the application are leak
2128      * suspects. - Kill the dialogs that are still around after the timer specified.
2129      *
2130      * @return Audit report, null if no dialog leaks were found
2131      */
auditDialogs(Set activeCallIDs, long leakedDialogTimer)2132     private String auditDialogs(Set activeCallIDs, long leakedDialogTimer) {
2133         String auditReport = "  Leaked dialogs:\n";
2134         int leakedDialogs = 0;
2135         long currentTime = System.currentTimeMillis();
2136 
2137         // Make a shallow copy of the dialog list.
2138         // This copy will remain intact as leaked dialogs are removed by the
2139         // stack.
2140         LinkedList dialogs;
2141         synchronized (dialogTable) {
2142             dialogs = new LinkedList(dialogTable.values());
2143         }
2144 
2145         // Iterate through the dialogDialog, get the callID of each dialog and
2146         // check if it's in the
2147         // list of active calls passed by the application. If it isn't, start
2148         // the timer on it.
2149         // If the timer has expired, kill the dialog.
2150         Iterator it = dialogs.iterator();
2151         while (it.hasNext()) {
2152             // Get the next dialog
2153             SIPDialog itDialog = (SIPDialog) it.next();
2154 
2155             // Get the call id associated with this dialog
2156             CallIdHeader callIdHeader = (itDialog != null ? itDialog.getCallId() : null);
2157             String callID = (callIdHeader != null ? callIdHeader.getCallId() : null);
2158 
2159             // Check if the application knows about this call id
2160             if (itDialog != null && callID != null && !activeCallIDs.contains(callID)) {
2161                 // Application doesn't know anything about this dialog...
2162                 if (itDialog.auditTag == 0) {
2163                     // Mark this dialog as suspect
2164                     itDialog.auditTag = currentTime;
2165                 } else {
2166                     // We already audited this dialog before. Check if his
2167                     // time's up.
2168                     if (currentTime - itDialog.auditTag >= leakedDialogTimer) {
2169                         // Leaked dialog found
2170                         leakedDialogs++;
2171 
2172                         // Generate report
2173                         DialogState dialogState = itDialog.getState();
2174                         String dialogReport = "dialog id: " + itDialog.getDialogId()
2175                                 + ", dialog state: "
2176                                 + (dialogState != null ? dialogState.toString() : "null");
2177                         auditReport += "    " + dialogReport + "\n";
2178 
2179                         // Kill it
2180                         itDialog.setState(SIPDialog.TERMINATED_STATE);
2181                         if (stackLogger.isLoggingEnabled())
2182                         	stackLogger.logDebug("auditDialogs: leaked " + dialogReport);
2183                     }
2184                 }
2185             }
2186         }
2187 
2188         // Return final report
2189         if (leakedDialogs > 0) {
2190             auditReport += "    Total: " + Integer.toString(leakedDialogs)
2191                     + " leaked dialogs detected and removed.\n";
2192         } else {
2193             auditReport = null;
2194         }
2195         return auditReport;
2196     }
2197 
2198     /**
2199      * Audits SIP transactions for leaks
2200      *
2201      * @return Audit report, null if no transaction leaks were found
2202      */
auditTransactions(ConcurrentHashMap transactionsMap, long a_nLeakedTransactionTimer)2203     private String auditTransactions(ConcurrentHashMap transactionsMap,
2204             long a_nLeakedTransactionTimer) {
2205         String auditReport = "  Leaked transactions:\n";
2206         int leakedTransactions = 0;
2207         long currentTime = System.currentTimeMillis();
2208 
2209         // Make a shallow copy of the transaction list.
2210         // This copy will remain intact as leaked transactions are removed by
2211         // the stack.
2212         LinkedList transactionsList = new LinkedList(transactionsMap.values());
2213 
2214         // Iterate through our copy
2215         Iterator it = transactionsList.iterator();
2216         while (it.hasNext()) {
2217             SIPTransaction sipTransaction = (SIPTransaction) it.next();
2218             if (sipTransaction != null) {
2219                 if (sipTransaction.auditTag == 0) {
2220                     // First time we see this transaction. Mark it as audited.
2221                     sipTransaction.auditTag = currentTime;
2222                 } else {
2223                     // We've seen this transaction before. Check if his time's
2224                     // up.
2225                     if (currentTime - sipTransaction.auditTag >= a_nLeakedTransactionTimer) {
2226                         // Leaked transaction found
2227                         leakedTransactions++;
2228 
2229                         // Generate some report
2230                         TransactionState transactionState = sipTransaction.getState();
2231                         SIPRequest origRequest = sipTransaction.getOriginalRequest();
2232                         String origRequestMethod = (origRequest != null ? origRequest.getMethod()
2233                                 : null);
2234                         String transactionReport = sipTransaction.getClass().getName()
2235                                 + ", state: "
2236                                 + (transactionState != null ? transactionState.toString()
2237                                         : "null") + ", OR: "
2238                                 + (origRequestMethod != null ? origRequestMethod : "null");
2239                         auditReport += "    " + transactionReport + "\n";
2240 
2241                         // Kill it
2242                         removeTransaction(sipTransaction);
2243                         if (isLoggingEnabled())
2244                         	stackLogger.logDebug("auditTransactions: leaked " + transactionReport);
2245                     }
2246                 }
2247             }
2248         }
2249 
2250         // Return final report
2251         if (leakedTransactions > 0) {
2252             auditReport += "    Total: " + Integer.toString(leakedTransactions)
2253                     + " leaked transactions detected and removed.\n";
2254         } else {
2255             auditReport = null;
2256         }
2257         return auditReport;
2258     }
2259 
setNon2XXAckPassedToListener(boolean passToListener)2260     public void setNon2XXAckPassedToListener(boolean passToListener) {
2261         this.non2XXAckPassedToListener = passToListener;
2262     }
2263 
2264     /**
2265      * @return the non2XXAckPassedToListener
2266      */
isNon2XXAckPassedToListener()2267     public boolean isNon2XXAckPassedToListener() {
2268         return non2XXAckPassedToListener;
2269     }
2270 
2271     /**
2272      * Get the count of client transactions that is not in the completed or terminated state.
2273      *
2274      * @return the activeClientTransactionCount
2275      */
getActiveClientTransactionCount()2276     public int getActiveClientTransactionCount() {
2277         return activeClientTransactionCount.get();
2278     }
2279 
isRfc2543Supported()2280     public boolean isRfc2543Supported() {
2281 
2282         return this.rfc2543Supported;
2283     }
2284 
isCancelClientTransactionChecked()2285     public boolean isCancelClientTransactionChecked() {
2286         return this.cancelClientTransactionChecked;
2287     }
2288 
isRemoteTagReassignmentAllowed()2289     public boolean isRemoteTagReassignmentAllowed() {
2290         return this.remoteTagReassignmentAllowed;
2291     }
2292 
2293     /**
2294      * This method is slated for addition to the next spec revision.
2295      *
2296      *
2297      * @return -- the collection of dialogs that is being managed by the stack.
2298      */
getDialogs()2299     public Collection<Dialog> getDialogs() {
2300         HashSet<Dialog> dialogs = new HashSet<Dialog>();
2301         dialogs.addAll(this.dialogTable.values());
2302         dialogs.addAll(this.earlyDialogTable.values());
2303         return dialogs;
2304     }
2305 
2306     /**
2307      *
2308      * @return -- the collection of dialogs matching the state that is being managed by the stack.
2309      */
getDialogs(DialogState state)2310     public Collection<Dialog> getDialogs(DialogState state) {
2311         HashSet<Dialog> matchingDialogs = new HashSet<Dialog>();
2312         if (DialogState.EARLY.equals(state)) {
2313             matchingDialogs.addAll(this.earlyDialogTable.values());
2314         } else {
2315             Collection<SIPDialog> dialogs = dialogTable.values();
2316             for (SIPDialog dialog : dialogs) {
2317                 if (dialog.getState() != null && dialog.getState().equals(state)) {
2318                     matchingDialogs.add(dialog);
2319                 }
2320             }
2321         }
2322         return matchingDialogs;
2323     }
2324 
2325     /**
2326      * Get the Replaced Dialog from the stack.
2327      *
2328      * @param replacesHeader -- the header that references the dialog being replaced.
2329      */
getReplacesDialog(ReplacesHeader replacesHeader)2330     public Dialog getReplacesDialog(ReplacesHeader replacesHeader) {
2331         String cid = replacesHeader.getCallId();
2332         String fromTag = replacesHeader.getFromTag();
2333         String toTag = replacesHeader.getToTag();
2334 
2335         StringBuffer dialogId = new StringBuffer(cid);
2336 
2337         // retval.append(COLON).append(to.getUserAtHostPort());
2338         if (toTag != null) {
2339             dialogId.append(":");
2340             dialogId.append(toTag);
2341         }
2342         // retval.append(COLON).append(from.getUserAtHostPort());
2343         if (fromTag != null) {
2344             dialogId.append(":");
2345             dialogId.append(fromTag);
2346         }
2347         String did = dialogId.toString().toLowerCase();
2348         if (stackLogger.isLoggingEnabled())
2349         	stackLogger.logDebug("Looking for dialog " + did);
2350         /*
2351          * Check if we can find this dialog in our dialog table.
2352          */
2353         Dialog replacesDialog =  this.dialogTable.get(did);
2354         /*
2355          * This could be a forked dialog. Search for it.
2356          */
2357         if ( replacesDialog == null ) {
2358            for ( SIPClientTransaction ctx : this.clientTransactionTable.values()) {
2359                if ( ctx.getDialog(did) != null ) {
2360                    replacesDialog = ctx.getDialog(did);
2361                    break;
2362                }
2363            }
2364         }
2365 
2366         return replacesDialog;
2367     }
2368 
2369     /**
2370      * Get the Join Dialog from the stack.
2371      *
2372      * @param joinHeader -- the header that references the dialog being joined.
2373      */
getJoinDialog(JoinHeader joinHeader)2374     public Dialog getJoinDialog(JoinHeader joinHeader) {
2375         String cid = joinHeader.getCallId();
2376         String fromTag = joinHeader.getFromTag();
2377         String toTag = joinHeader.getToTag();
2378 
2379         StringBuffer retval = new StringBuffer(cid);
2380 
2381         // retval.append(COLON).append(to.getUserAtHostPort());
2382         if (toTag != null) {
2383             retval.append(":");
2384             retval.append(toTag);
2385         }
2386         // retval.append(COLON).append(from.getUserAtHostPort());
2387         if (fromTag != null) {
2388             retval.append(":");
2389             retval.append(fromTag);
2390         }
2391         return this.dialogTable.get(retval.toString().toLowerCase());
2392     }
2393 
2394     /**
2395      * @param timer the timer to set
2396      */
setTimer(Timer timer)2397     public void setTimer(Timer timer) {
2398         this.timer = timer;
2399     }
2400 
2401     /**
2402      * @return the timer
2403      */
getTimer()2404     public Timer getTimer() {
2405         return timer;
2406     }
2407 
2408 
2409     /**
2410      * Size of the receive UDP buffer. This property affects performance under load. Bigger buffer
2411      * is better under load.
2412      *
2413      * @return
2414      */
getReceiveUdpBufferSize()2415 	public int getReceiveUdpBufferSize() {
2416 		return receiveUdpBufferSize;
2417 	}
2418 
2419     /**
2420      * Size of the receive UDP buffer. This property affects performance under load. Bigger buffer
2421      * is better under load.
2422      *
2423      * @return
2424      */
setReceiveUdpBufferSize(int receiveUdpBufferSize)2425 	public void setReceiveUdpBufferSize(int receiveUdpBufferSize) {
2426 		this.receiveUdpBufferSize = receiveUdpBufferSize;
2427 	}
2428 
2429     /**
2430      * Size of the send UDP buffer. This property affects performance under load. Bigger buffer
2431      * is better under load.
2432      *
2433      * @return
2434      */
getSendUdpBufferSize()2435 	public int getSendUdpBufferSize() {
2436 		return sendUdpBufferSize;
2437 	}
2438 
2439     /**
2440      * Size of the send UDP buffer. This property affects performance under load. Bigger buffer
2441      * is better under load.
2442      *
2443      * @return
2444      */
setSendUdpBufferSize(int sendUdpBufferSize)2445 	public void setSendUdpBufferSize(int sendUdpBufferSize) {
2446 		this.sendUdpBufferSize = sendUdpBufferSize;
2447 	}
2448 
2449 	/**
2450 	 * @param stackLogger the stackLogger to set
2451 	 */
setStackLogger(StackLogger stackLogger)2452 	public void setStackLogger(StackLogger stackLogger) {
2453 		this.stackLogger = stackLogger;
2454 	}
2455 
2456 	 /**
2457 	  * Flag that reqests checking of branch IDs on responses.
2458 	  *
2459 	  * @return
2460 	  */
checkBranchId()2461 	 public boolean checkBranchId() {
2462 	       return this.checkBranchId;
2463 	 }
2464 
2465     /**
2466      * @param logStackTraceOnMessageSend the logStackTraceOnMessageSend to set
2467      */
setLogStackTraceOnMessageSend(boolean logStackTraceOnMessageSend)2468     public void setLogStackTraceOnMessageSend(boolean logStackTraceOnMessageSend) {
2469         this.logStackTraceOnMessageSend = logStackTraceOnMessageSend;
2470     }
2471 
2472     /**
2473      * @return the logStackTraceOnMessageSend
2474      */
isLogStackTraceOnMessageSend()2475     public boolean isLogStackTraceOnMessageSend() {
2476         return logStackTraceOnMessageSend;
2477     }
2478 
setDeliverDialogTerminatedEventForNullDialog()2479     public void setDeliverDialogTerminatedEventForNullDialog() {
2480         this.isDialogTerminatedEventDeliveredForNullDialog = true;
2481     }
2482 
addForkedClientTransaction(SIPClientTransaction clientTransaction)2483     public void addForkedClientTransaction(SIPClientTransaction clientTransaction) {
2484         this.forkedClientTransactionTable.put(clientTransaction.getTransactionId(), clientTransaction );
2485     }
2486 
getForkedTransaction(String transactionId)2487     public SIPClientTransaction getForkedTransaction(String transactionId) {
2488         return this.forkedClientTransactionTable.get(transactionId);
2489     }
2490 
2491 
2492 }
2493