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 (at) 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 371 public PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle) { 372 threadHandle = a_oThreadHandle; 373 } 374 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 401 public RemoveForkedTransactionTimerTask(SIPClientTransaction sipClientTransaction ) { 402 this.clientTransaction = sipClientTransaction; 403 } 404 405 @Override 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 */ 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 */ 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 */ 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 */ 540 public void disableLogging() { 541 this.getStackLogger().disableLogging(); 542 } 543 544 /** 545 * Globally enable message logging ( for debugging) 546 * 547 */ 548 public void enableLogging() { 549 this.getStackLogger().enableLogging(); 550 } 551 552 /** 553 * Print the dialog table. 554 * 555 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1840 protected void setRouter(Router router) { 1841 this.router = router; 1842 } 1843 1844 /** 1845 * Get the router algorithm. 1846 * 1847 * @return Router router 1848 */ 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 */ 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 */ 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 */ 1889 protected void addMessageProcessor(MessageProcessor newMessageProcessor) throws IOException { 1890 synchronized (messageProcessors) { 1891 // Suggested changes by Jeyashankher, jai (at) 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 2260 public void setNon2XXAckPassedToListener(boolean passToListener) { 2261 this.non2XXAckPassedToListener = passToListener; 2262 } 2263 2264 /** 2265 * @return the non2XXAckPassedToListener 2266 */ 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 */ 2276 public int getActiveClientTransactionCount() { 2277 return activeClientTransactionCount.get(); 2278 } 2279 2280 public boolean isRfc2543Supported() { 2281 2282 return this.rfc2543Supported; 2283 } 2284 2285 public boolean isCancelClientTransactionChecked() { 2286 return this.cancelClientTransactionChecked; 2287 } 2288 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 */ 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 */ 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 */ 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 */ 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 */ 2397 public void setTimer(Timer timer) { 2398 this.timer = timer; 2399 } 2400 2401 /** 2402 * @return the timer 2403 */ 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 */ 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 */ 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 */ 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 */ 2445 public void setSendUdpBufferSize(int sendUdpBufferSize) { 2446 this.sendUdpBufferSize = sendUdpBufferSize; 2447 } 2448 2449 /** 2450 * @param stackLogger the stackLogger to set 2451 */ 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 */ 2461 public boolean checkBranchId() { 2462 return this.checkBranchId; 2463 } 2464 2465 /** 2466 * @param logStackTraceOnMessageSend the logStackTraceOnMessageSend to set 2467 */ 2468 public void setLogStackTraceOnMessageSend(boolean logStackTraceOnMessageSend) { 2469 this.logStackTraceOnMessageSend = logStackTraceOnMessageSend; 2470 } 2471 2472 /** 2473 * @return the logStackTraceOnMessageSend 2474 */ 2475 public boolean isLogStackTraceOnMessageSend() { 2476 return logStackTraceOnMessageSend; 2477 } 2478 2479 public void setDeliverDialogTerminatedEventForNullDialog() { 2480 this.isDialogTerminatedEventDeliveredForNullDialog = true; 2481 } 2482 2483 public void addForkedClientTransaction(SIPClientTransaction clientTransaction) { 2484 this.forkedClientTransactionTable.put(clientTransaction.getTransactionId(), clientTransaction ); 2485 } 2486 2487 public SIPClientTransaction getForkedTransaction(String transactionId) { 2488 return this.forkedClientTransactionTable.get(transactionId); 2489 } 2490 2491 2492 } 2493