/* * Conditions Of Use * * This software was developed by employees of the National Institute of * Standards and Technology (NIST), an agency of the Federal Government. * Pursuant to title 15 Untied States Code Section 105, works of NIST * employees are not subject to copyright protection in the United States * and are considered to be in the public domain. As a result, a formal * license is not needed to use the software. * * This software is provided by NIST as a service and is expressly * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT * AND DATA ACCURACY. NIST does not warrant or make any representations * regarding the use of the software or the results thereof, including but * not limited to the correctness, accuracy, reliability or usefulness of * the software. * * Permission to use this software is contingent upon your acceptance * of the terms of this agreement * * . * */ package gov.nist.javax.sip; import gov.nist.core.ServerLogger; import gov.nist.core.StackLogger; import gov.nist.core.net.AddressResolver; import gov.nist.core.net.NetworkLayer; import gov.nist.core.net.SslNetworkLayer; import gov.nist.javax.sip.clientauthutils.AccountManager; import gov.nist.javax.sip.clientauthutils.AuthenticationHelper; import gov.nist.javax.sip.clientauthutils.AuthenticationHelperImpl; import gov.nist.javax.sip.clientauthutils.SecureAccountManager; import gov.nist.javax.sip.parser.StringMsgParser; import gov.nist.javax.sip.stack.DefaultMessageLogFactory; import gov.nist.javax.sip.stack.DefaultRouter; import gov.nist.javax.sip.stack.MessageProcessor; import gov.nist.javax.sip.stack.SIPTransactionStack; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.util.Hashtable; import java.util.LinkedList; import java.util.Properties; import java.util.StringTokenizer; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.PeerUnavailableException; import javax.sip.ProviderDoesNotExistException; import javax.sip.SipException; import javax.sip.SipListener; import javax.sip.SipProvider; import javax.sip.SipStack; import javax.sip.TransportNotSupportedException; import javax.sip.address.Router; import javax.sip.header.HeaderFactory; import javax.sip.message.Request; /** * Implementation of SipStack. * * The JAIN-SIP stack is initialized by a set of properties (see the JAIN SIP * documentation for an explanation of these properties * {@link javax.sip.SipStack} ). In addition to these, the following are * meaningful properties for the NIST SIP stack (specify these in the property * array when you create the JAIN-SIP statck): * * *
  • gov.nist.javax.sip.IS_BACK_TO_BACK_USER_AGENT = [true|false]
    * Default is false This property controls a setting on the Dialog * objects that the stack manages. Pure B2BUA applications should set this flag * to true. This property can also be set on a per-dialog basis. * Setting this to true imposes serialization on re-INVITE and makes * the sending of re-INVITEs asynchronous. The sending of re-INVITE is * controlled as follows : If the previous in-DIALOG request was an invite * ClientTransaction then the next re-INVITEs that uses the dialog will wait * till an ACK has been sent before admitting the new re-INVITE. If the previous * in-DIALOG transaction was a INVITE ServerTransaction then Dialog waits for * ACK before re-INVITE is allowed to be sent. If a dialog is not ACKed within * 32 seconds, then the dialog is torn down and a BYE sent to the peer.
  • *
  • gov.nist.javax.sip.MAX_MESSAGE_SIZE = integer
    * Maximum size of content that a TCP connection can read. Must be at least 4K. * Default is "infinity" -- ie. no limit. This is to prevent DOS attacks * launched by writing to a TCP connection until the server chokes.
  • * *
  • gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG = [true|false]
    * If set to false (the default), the application does NOT get notified when a Dialog in the * NULL state is terminated. ( Dialogs in the NULL state are not associated with an actual SIP Dialog. * They are a programming convenience. A Dialog is in the NULL state before the first response for the * Dialog forming Transaction). If set to true, the SipListener will get a DialogTerminatedEvent * when a Dialog in the NULL state is terminated. *
  • * *
  • gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS = [true|false]
    * Default value is true. Setting this to false makes the Stack close the server * socket after a Server Transaction goes to the TERMINATED state. This allows a * server to protectect against TCP based Denial of Service attacks launched by * clients (ie. initiate hundreds of client transactions). If true (default * action), the stack will keep the socket open so as to maximize performance at * the expense of Thread and memory resources - leaving itself open to DOS * attacks.
  • * * *
  • gov.nist.javax.sip.CACHE_CLIENT_CONNECTIONS = [true|false]
    * Default value is true. Setting this to false makes the Stack close the server * socket after a Client Transaction goes to the TERMINATED state. This allows a * client release any buffers threads and socket connections associated with a * client transaction after the transaction has terminated at the expense of * performance.
  • * *
  • gov.nist.javax.sip.THREAD_POOL_SIZE = integer
    * Concurrency control for number of simultaneous active threads. If * unspecificed, the default is "infinity". This feature is useful if you are * trying to build a container. * * *
  • gov.nist.javax.sip.REENTRANT_LISTENER = true|false
    * Default is false. Set to true if the listener is re-entrant. If the listener * is re-entrant then the stack manages a thread pool and synchronously calls * the listener from the same thread which read the message. Multiple * transactions may concurrently receive messages and this will result in * multiple threads being active in the listener at the same time. The listener * has to be written with this in mind. If you want good performance on a * multithreaded machine write your listener to be re-entrant and set this * property to be true
  • * *
  • gov.nist.javax.sip.MAX_CONNECTIONS = integer
    * Max number of simultaneous TCP connections handled by stack.
  • * *
  • gov.nist.javax.sip.MAX_SERVER_TRANSACTIONS = integer
    * Maximum size of server transaction table. The low water mark is 80% of the * high water mark. Requests are selectively dropped in the lowater mark to * highwater mark range. Requests are unconditionally accepted if the table is * smaller than the low water mark. The default highwater mark is 5000
  • * *
  • gov.nist.javax.sip.MAX_CLIENT_TRANSACTIONS = integer
    * Max number of active client transactions before the caller blocks and waits * for the number to drop below a threshold. Default is unlimited, i.e. the * caller never blocks and waits for a client transaction to become available * (i.e. it does its own resource management in the application).
  • * *
  • gov.nist.javax.sip.PASS_INVITE_NON_2XX_ACK_TO_LISTENER = true|false *
    * If true then the listener will see the ACK for non-2xx responses for server * transactions. This is not standard behavior per RFC 3261 (INVITE server * transaction state machine) but this is a useful flag for testing. The TCK * uses this flag for example.
  • * *
  • gov.nist.javax.sip.MAX_LISTENER_RESPONSE_TIME = Integer
    * Max time (seconds) before sending a response to a server transaction. If a * response is not sent within this time period, the transaction will be deleted * by the stack. Default time is "infinity" - i.e. if the listener never * responds, the stack will hang on to a reference for the transaction and * result in a memory leak. * *
  • gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_ACK = [true|false] *
    * Default is false. ACK Server Transaction is a Pseuedo-transaction. * If you want termination notification on ACK transactions (so all server * transactions can be handled uniformly in user code during cleanup), then set * this flag to true.
  • * *
  • gov.nist.javax.sip.READ_TIMEOUT = integer
    * This is relevant for incoming TCP connections to prevent starvation at the * server. This defines the timeout in miliseconds between successive reads * after the first byte of a SIP message is read by the stack. All the sip * headers must be delivered in this interval and each successive buffer must be * of the content delivered in this interval. Default value is -1 (ie. the stack * is wide open to starvation attacks) and the client can be as slow as it wants * to be.
  • * *
  • gov.nist.javax.sip.NETWORK_LAYER = classpath
    * This is an EXPERIMENTAL property (still under active devlopment). Defines a * network layer that allows a client to have control over socket allocations * and monitoring of socket activity. A network layer should implement * gov.nist.core.net.NetworkLayer. The default implementation simply acts as a * wrapper for the standard java.net socket layer. This functionality is still * under active development (may be extended to support security and other * features).
  • * *
  • gov.nist.javax.sip.ADDRESS_RESOLVER = classpath
    * The fully qualified class path for an implementation of the AddressResolver * interface. The AddressResolver allows you to support lookup schemes for * addresses that are not directly resolvable to IP adresses using * getHostByName. Specifying your own address resolver allows you to customize * address lookup. The default address resolver is a pass-through address * resolver (i.e. just returns the input string without doing a resolution). See * gov.nist.javax.sip.DefaultAddressResolver.
  • * *
  • gov.nist.javax.sip.AUTO_GENERATE_TIMESTAMP= [true| false]
    * (default is false) Automatically generate a getTimeOfDay timestamp for a * retransmitted request if the original request contained a timestamp. This is * useful for profiling.
  • * *
  • gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS = long
    * Defines how often the application intends to audit the SIP Stack about the * health of its internal threads (the property specifies the time in * miliseconds between successive audits). The audit allows the application to * detect catastrophic failures like an internal thread terminating because of * an exception or getting stuck in a deadlock condition. Events like these will * make the stack inoperable and therefore require immediate action from the * application layer (e.g., alarms, traps, reboot, failover, etc.) Thread audits * are disabled by default. If this property is not specified, audits will * remain disabled. An example of how to use this property is in * src/examples/threadaudit.
  • * * * *
  • gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY = * [true|false]
    * Default is false If set to true, when you are creating a * message from a String, the MessageFactory will compute the content * length from the message content and ignore the provided content length * parameter in the Message. Otherwise, it will use the content length supplied * and generate a parse exception if the content is truncated. * *
  • gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED = [true|false] *
    * Default is true. This flag is added in support of load balancers or * failover managers where you may want to cancel ongoing transactions from a * different stack than the original stack. If set to false then the * CANCEL client transaction is not checked for the existence of the INVITE or * the state of INVITE when you send the CANCEL request. Hence you can CANCEL an * INVITE from a different stack than the INVITE. You can also create a CANCEL * client transaction late and send it out after the INVITE server transaction * has been Terminated. Clearly this will result in protocol errors. Setting the * flag to true ( default ) enables you to avoid common protocol errors.
  • * *
  • gov.nist.javax.sip.IS_BACK_TO_BACK_USER_AGENT = [true|false]
    * Default is false This property controls a setting on the Dialog * objects that the stack manages. Pure B2BUA applications should set this flag * to true. This property can also be set on a per-dialog basis. * Setting this to true imposes serialization on re-INVITE and makes * the sending of re-INVITEs asynchronous. The sending of re-INVITE is * controlled as follows : If the previous in-DIALOG request was an invite * ClientTransaction then the next re-INVITEs that uses the dialog will wait * till an ACK has been sent before admitting the new re-INVITE. If the previous * in-DIALOG transaction was a INVITE ServerTransaction then Dialog waits for * ACK before re-INVITE is allowed to be sent. If a dialog is not ACKed within * 32 seconds, then the dialog is torn down and a BYE sent to the peer.
  • * * *
  • gov.nist.javax.sip.RECEIVE_UDP_BUFFER_SIZE = int
    * Default is 8*1024. This property control the size of the UDP buffer * used for SIP messages. Under load, if the buffer capacity is overflown the * messages are dropped causing retransmissions, further increasing the load and * causing even more retransmissions. Good values to this property for servers * is a big number in the order of 8*8*1024.
  • * *
  • gov.nist.javax.sip.SEND_UDP_BUFFER_SIZE = int
    * Default is 8*1024. This property control the size of the UDP buffer * used for SIP messages. Under load, if the buffer capacity is overflown the * messages are dropped causing retransmissions, further increasing the load and * causing even more retransmissions. Good values to this property for servers * is a big number in the order of 8*8*1024 or higher.
  • * *
  • gov.nist.javax.sip.CONGESTION_CONTROL_ENABLED = boolean Defailt * is true. If set to true stack will enforce queue length limitation for UDP. * The Max queue size is 5000 messages. The minimum queue size is 2500 messages. *
  • * *
  • gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY = [true|false]
    * Default is false. This flag is added to allow Sip Listeners to * receive all NOTIFY requests including those that are not part of a valid * dialog.
  • * *
  • gov.nist.javax.sip.REJECT_STRAY_RESPONSES = [true|false] Default * is false A flag that checks responses to test whether the response * corresponds to a via header that was previously generated by us. Note that * setting this flag implies that the stack will take control over setting the * VIA header for Sip Requests sent through the stack. The stack will attach a * suffix to the VIA header branch and check any response arriving at the stack * to see if that response suffix is present. If it is not present, then the * stack will silently drop the response.
  • * *
  • gov.nist.javax.sip.MAX_FORK_TIME_SECONDS = integer Maximum time for which the original * transaction for which a forked response is received is tracked. This property * is only relevant to Dialog Stateful applications ( User Agents or B2BUA). * When a forked response is received in this time interval from when the original * INVITE client transaction was sent, the stack will place the original INVITE * client transction in the ResponseEventExt and deliver that to the application. * The event handler can get the original transaction from this event.
  • * * *
  • gov.nist.javax.sip.TLS_CLIENT_PROTOCOLS = String * Comma-separated list of protocols to use when creating outgoing TLS connections. * The default is "SSLv3, SSLv2Hello, TLSv1". * Some servers do not support SSLv2Hello, so override to "SSLv3, TLSv1". *
  • *
  • javax.net.ssl.keyStore = fileName
    * Default is NULL. If left undefined the keyStore and trustStore will * be left to the java runtime defaults. If defined, any TLS sockets created * (client and server) will use the key store provided in the fileName. The * trust store will default to the same store file. A password must be provided * to access the keyStore using the following property:
    * * properties.setProperty("javax.net.ssl.keyStorePassword", "<password>"); *
    * The trust store can be changed, to a separate file with the following * setting:
    * * properties.setProperty("javax.net.ssl.trustStore", "<trustStoreFileName location>"); *
    * If the trust store property is provided the password on the trust store must * be the same as the key store.
    *
    * Note that the stack supports the extensions that are defined in * SipStackExt. These will be supported in the next release of JAIN-SIP. You * should only use the extensions that are defined in this class. * * * @version 1.2 $Revision: 1.115 $ $Date: 2010/01/10 00:13:14 $ * * @author M. Ranganathan
    * * * * */ public class SipStackImpl extends SIPTransactionStack implements javax.sip.SipStack, SipStackExt { private EventScanner eventScanner; private Hashtable listeningPoints; private LinkedList sipProviders; /** * Max datagram size. */ public static final Integer MAX_DATAGRAM_SIZE = 8 * 1024; // Flag to indicate that the listener is re-entrant and hence // Use this flag with caution. boolean reEntrantListener; SipListener sipListener; // If set to true then a transaction terminated event is // delivered for ACK transactions. boolean deliverTerminatedEventForAck = false; // If set to true then the application want to receive // unsolicited NOTIFYs, ie NOTIFYs that don't match any dialog boolean deliverUnsolicitedNotify = false; // Stack semaphore (global lock). private Semaphore stackSemaphore = new Semaphore(1); // RFC3261: TLS_RSA_WITH_AES_128_CBC_SHA MUST be supported // RFC3261: TLS_RSA_WITH_3DES_EDE_CBC_SHA SHOULD be supported for backwards // compat private String[] cipherSuites = { "TLS_RSA_WITH_AES_128_CBC_SHA", // AES difficult to get with // c++/Windows // "TLS_RSA_WITH_3DES_EDE_CBC_SHA", // Unsupported by Sun impl, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", // For backwards comp., C++ // JvB: patch from Sebastien Mazy, issue with mismatching // ciphersuites "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", }; // Supported protocols for TLS client: can be overridden by application private String[] enabledProtocols = { "SSLv3", "SSLv2Hello", "TLSv1" }; /** * Creates a new instance of SipStackImpl. */ protected SipStackImpl() { super(); NistSipMessageFactoryImpl msgFactory = new NistSipMessageFactoryImpl( this); super.setMessageFactory(msgFactory); this.eventScanner = new EventScanner(this); this.listeningPoints = new Hashtable(); this.sipProviders = new LinkedList(); } /** * ReInitialize the stack instance. */ private void reInitialize() { super.reInit(); this.eventScanner = new EventScanner(this); this.listeningPoints = new Hashtable(); this.sipProviders = new LinkedList(); this.sipListener = null; } /** * Return true if automatic dialog support is enabled for this stack. * * @return boolean, true if automatic dialog support is enabled for this * stack */ boolean isAutomaticDialogSupportEnabled() { return super.isAutomaticDialogSupportEnabled; } /** * Constructor for the stack. * * @param configurationProperties * -- stack configuration properties including NIST-specific * extensions. * @throws PeerUnavailableException */ public SipStackImpl(Properties configurationProperties) throws PeerUnavailableException { this(); String address = configurationProperties .getProperty("javax.sip.IP_ADDRESS"); try { /** Retrieve the stack IP address */ if (address != null) { // In version 1.2 of the spec the IP address is // associated with the listening point and // is not madatory. super.setHostAddress(address); } } catch (java.net.UnknownHostException ex) { throw new PeerUnavailableException("bad address " + address); } /** Retrieve the stack name */ String name = configurationProperties .getProperty("javax.sip.STACK_NAME"); if (name == null) throw new PeerUnavailableException("stack name is missing"); super.setStackName(name); String stackLoggerClassName = configurationProperties .getProperty("gov.nist.javax.sip.STACK_LOGGER"); // To log debug messages. if (stackLoggerClassName == null) stackLoggerClassName = "gov.nist.core.LogWriter"; try { Class stackLoggerClass = Class.forName(stackLoggerClassName); Class[] constructorArgs = new Class[0]; Constructor cons = stackLoggerClass .getConstructor(constructorArgs); Object[] args = new Object[0]; StackLogger stackLogger = (StackLogger) cons.newInstance(args); stackLogger.setStackProperties(configurationProperties); super.setStackLogger(stackLogger); } catch (InvocationTargetException ex1) { throw new IllegalArgumentException( "Cound not instantiate stack logger " + stackLoggerClassName + "- check that it is present on the classpath and that there is a no-args constructor defined", ex1); } catch (Exception ex) { throw new IllegalArgumentException( "Cound not instantiate stack logger " + stackLoggerClassName + "- check that it is present on the classpath and that there is a no-args constructor defined", ex); } String serverLoggerClassName = configurationProperties .getProperty("gov.nist.javax.sip.SERVER_LOGGER"); // To log debug messages. if (serverLoggerClassName == null) serverLoggerClassName = "gov.nist.javax.sip.stack.ServerLog"; try { Class serverLoggerClass = Class .forName(serverLoggerClassName); Class[] constructorArgs = new Class[0]; Constructor cons = serverLoggerClass .getConstructor(constructorArgs); Object[] args = new Object[0]; this.serverLogger = (ServerLogger) cons.newInstance(args); serverLogger.setSipStack(this); serverLogger.setStackProperties(configurationProperties); } catch (InvocationTargetException ex1) { throw new IllegalArgumentException( "Cound not instantiate server logger " + stackLoggerClassName + "- check that it is present on the classpath and that there is a no-args constructor defined", ex1); } catch (Exception ex) { throw new IllegalArgumentException( "Cound not instantiate server logger " + stackLoggerClassName + "- check that it is present on the classpath and that there is a no-args constructor defined", ex); } // Default router -- use this for routing SIP URIs. // Our router does not do DNS lookups. this.outboundProxy = configurationProperties .getProperty("javax.sip.OUTBOUND_PROXY"); this.defaultRouter = new DefaultRouter(this, outboundProxy); /** Retrieve the router path */ String routerPath = configurationProperties .getProperty("javax.sip.ROUTER_PATH"); if (routerPath == null) routerPath = "gov.nist.javax.sip.stack.DefaultRouter"; try { Class routerClass = Class.forName(routerPath); Class[] constructorArgs = new Class[2]; constructorArgs[0] = javax.sip.SipStack.class; constructorArgs[1] = String.class; Constructor cons = routerClass.getConstructor(constructorArgs); Object[] args = new Object[2]; args[0] = (SipStack) this; args[1] = outboundProxy; Router router = (Router) cons.newInstance(args); super.setRouter(router); } catch (InvocationTargetException ex1) { getStackLogger() .logError( "could not instantiate router -- invocation target problem", (Exception) ex1.getCause()); throw new PeerUnavailableException( "Cound not instantiate router - check constructor", ex1); } catch (Exception ex) { getStackLogger().logError("could not instantiate router", (Exception) ex.getCause()); throw new PeerUnavailableException("Could not instantiate router", ex); } // The flag that indicates that the default router is to be ignored. String useRouterForAll = configurationProperties .getProperty("javax.sip.USE_ROUTER_FOR_ALL_URIS"); this.useRouterForAll = true; if (useRouterForAll != null) { this.useRouterForAll = "true".equalsIgnoreCase(useRouterForAll); } /* * Retrieve the EXTENSION Methods. These are used for instantiation of * Dialogs. */ String extensionMethods = configurationProperties .getProperty("javax.sip.EXTENSION_METHODS"); if (extensionMethods != null) { java.util.StringTokenizer st = new java.util.StringTokenizer( extensionMethods); while (st.hasMoreTokens()) { String em = st.nextToken(":"); if (em.equalsIgnoreCase(Request.BYE) || em.equalsIgnoreCase(Request.INVITE) || em.equalsIgnoreCase(Request.SUBSCRIBE) || em.equalsIgnoreCase(Request.NOTIFY) || em.equalsIgnoreCase(Request.ACK) || em.equalsIgnoreCase(Request.OPTIONS)) throw new PeerUnavailableException("Bad extension method " + em); else this.addExtensionMethod(em); } } String keyStoreFile = configurationProperties .getProperty("javax.net.ssl.keyStore"); String trustStoreFile = configurationProperties .getProperty("javax.net.ssl.trustStore"); if (keyStoreFile != null) { if (trustStoreFile == null) { trustStoreFile = keyStoreFile; } String keyStorePassword = configurationProperties .getProperty("javax.net.ssl.keyStorePassword"); try { this.networkLayer = new SslNetworkLayer(trustStoreFile, keyStoreFile, keyStorePassword.toCharArray(), configurationProperties .getProperty("javax.net.ssl.keyStoreType")); } catch (Exception e1) { getStackLogger().logError( "could not instantiate SSL networking", e1); } } // Set the auto dialog support flag. super.isAutomaticDialogSupportEnabled = configurationProperties .getProperty("javax.sip.AUTOMATIC_DIALOG_SUPPORT", "on") .equalsIgnoreCase("on"); super.isAutomaticDialogErrorHandlingEnabled = configurationProperties .getProperty("gov.nist.javax.sip.AUTOMATIC_DIALOG_ERROR_HANDLING","true") .equals(Boolean.TRUE.toString()); if ( super.isAutomaticDialogSupportEnabled ) { super.isAutomaticDialogErrorHandlingEnabled = true; } if (configurationProperties .getProperty("gov.nist.javax.sip.MAX_LISTENER_RESPONSE_TIME") != null) { super.maxListenerResponseTime = Integer .parseInt(configurationProperties .getProperty("gov.nist.javax.sip.MAX_LISTENER_RESPONSE_TIME")); if (super.maxListenerResponseTime <= 0) throw new PeerUnavailableException( "Bad configuration parameter gov.nist.javax.sip.MAX_LISTENER_RESPONSE_TIME : should be positive"); } else { super.maxListenerResponseTime = -1; } this.deliverTerminatedEventForAck = configurationProperties .getProperty( "gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_ACK", "false").equalsIgnoreCase("true"); this.deliverUnsolicitedNotify = configurationProperties.getProperty( "gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY", "false") .equalsIgnoreCase("true"); String forkedSubscriptions = configurationProperties .getProperty("javax.sip.FORKABLE_EVENTS"); if (forkedSubscriptions != null) { StringTokenizer st = new StringTokenizer(forkedSubscriptions); while (st.hasMoreTokens()) { String nextEvent = st.nextToken(); this.forkedEvents.add(nextEvent); } } // The following features are unique to the NIST implementation. /* * gets the NetworkLayer implementation, if any. Note that this is a * NIST only feature. */ final String NETWORK_LAYER_KEY = "gov.nist.javax.sip.NETWORK_LAYER"; if (configurationProperties.containsKey(NETWORK_LAYER_KEY)) { String path = configurationProperties .getProperty(NETWORK_LAYER_KEY); try { Class clazz = Class.forName(path); Constructor c = clazz.getConstructor(new Class[0]); networkLayer = (NetworkLayer) c.newInstance(new Object[0]); } catch (Exception e) { throw new PeerUnavailableException( "can't find or instantiate NetworkLayer implementation: " + path); } } final String ADDRESS_RESOLVER_KEY = "gov.nist.javax.sip.ADDRESS_RESOLVER"; if (configurationProperties.containsKey(ADDRESS_RESOLVER_KEY)) { String path = configurationProperties .getProperty(ADDRESS_RESOLVER_KEY); try { Class clazz = Class.forName(path); Constructor c = clazz.getConstructor(new Class[0]); this.addressResolver = (AddressResolver) c .newInstance(new Object[0]); } catch (Exception e) { throw new PeerUnavailableException( "can't find or instantiate AddressResolver implementation: " + path); } } String maxConnections = configurationProperties .getProperty("gov.nist.javax.sip.MAX_CONNECTIONS"); if (maxConnections != null) { try { this.maxConnections = new Integer(maxConnections).intValue(); } catch (NumberFormatException ex) { if (isLoggingEnabled()) getStackLogger().logError( "max connections - bad value " + ex.getMessage()); } } String threadPoolSize = configurationProperties .getProperty("gov.nist.javax.sip.THREAD_POOL_SIZE"); if (threadPoolSize != null) { try { this.threadPoolSize = new Integer(threadPoolSize).intValue(); } catch (NumberFormatException ex) { if (isLoggingEnabled()) this.getStackLogger().logError( "thread pool size - bad value " + ex.getMessage()); } } String serverTransactionTableSize = configurationProperties .getProperty("gov.nist.javax.sip.MAX_SERVER_TRANSACTIONS"); if (serverTransactionTableSize != null) { try { this.serverTransactionTableHighwaterMark = new Integer( serverTransactionTableSize).intValue(); this.serverTransactionTableLowaterMark = this.serverTransactionTableHighwaterMark * 80 / 100; // Lowater is 80% of highwater } catch (NumberFormatException ex) { if (isLoggingEnabled()) this.getStackLogger() .logError( "transaction table size - bad value " + ex.getMessage()); } } else { // Issue 256 : consistent with MAX_CLIENT_TRANSACTIONS, if the MAX_SERVER_TRANSACTIONS is not set // we assume the transaction table size can grow unlimited this.unlimitedServerTransactionTableSize = true; } String clientTransactionTableSize = configurationProperties .getProperty("gov.nist.javax.sip.MAX_CLIENT_TRANSACTIONS"); if (clientTransactionTableSize != null) { try { this.clientTransactionTableHiwaterMark = new Integer( clientTransactionTableSize).intValue(); this.clientTransactionTableLowaterMark = this.clientTransactionTableLowaterMark * 80 / 100; // Lowater is 80% of highwater } catch (NumberFormatException ex) { if (isLoggingEnabled()) this.getStackLogger() .logError( "transaction table size - bad value " + ex.getMessage()); } } else { this.unlimitedClientTransactionTableSize = true; } super.cacheServerConnections = true; String flag = configurationProperties .getProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS"); if (flag != null && "false".equalsIgnoreCase(flag.trim())) { super.cacheServerConnections = false; } super.cacheClientConnections = true; String cacheflag = configurationProperties .getProperty("gov.nist.javax.sip.CACHE_CLIENT_CONNECTIONS"); if (cacheflag != null && "false".equalsIgnoreCase(cacheflag.trim())) { super.cacheClientConnections = false; } String readTimeout = configurationProperties .getProperty("gov.nist.javax.sip.READ_TIMEOUT"); if (readTimeout != null) { try { int rt = Integer.parseInt(readTimeout); if (rt >= 100) { super.readTimeout = rt; } else { System.err.println("Value too low " + readTimeout); } } catch (NumberFormatException nfe) { // Ignore. if (isLoggingEnabled()) getStackLogger().logError("Bad read timeout " + readTimeout); } } // Get the address of the stun server. String stunAddr = configurationProperties .getProperty("gov.nist.javax.sip.STUN_SERVER"); if (stunAddr != null) this.getStackLogger().logWarning( "Ignoring obsolete property " + "gov.nist.javax.sip.STUN_SERVER"); String maxMsgSize = configurationProperties .getProperty("gov.nist.javax.sip.MAX_MESSAGE_SIZE"); try { if (maxMsgSize != null) { super.maxMessageSize = new Integer(maxMsgSize).intValue(); if (super.maxMessageSize < 4096) super.maxMessageSize = 4096; } else { // Allow for "infinite" size of message super.maxMessageSize = 0; } } catch (NumberFormatException ex) { if (isLoggingEnabled()) getStackLogger().logError( "maxMessageSize - bad value " + ex.getMessage()); } String rel = configurationProperties .getProperty("gov.nist.javax.sip.REENTRANT_LISTENER"); this.reEntrantListener = (rel != null && "true".equalsIgnoreCase(rel)); // Check if a thread audit interval is specified String interval = configurationProperties .getProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS"); if (interval != null) { try { // Make the monitored threads ping the auditor twice as fast as // the audits getThreadAuditor().setPingIntervalInMillisecs( Long.valueOf(interval).longValue() / 2); } catch (NumberFormatException ex) { if (isLoggingEnabled()) getStackLogger().logError( "THREAD_AUDIT_INTERVAL_IN_MILLISECS - bad value [" + interval + "] " + ex.getMessage()); } } // JvB: added property for testing this .setNon2XXAckPassedToListener(Boolean .valueOf( configurationProperties .getProperty( "gov.nist.javax.sip.PASS_INVITE_NON_2XX_ACK_TO_LISTENER", "false")).booleanValue()); this.generateTimeStampHeader = Boolean.valueOf( configurationProperties.getProperty( "gov.nist.javax.sip.AUTO_GENERATE_TIMESTAMP", "false")) .booleanValue(); String messageLogFactoryClasspath = configurationProperties .getProperty("gov.nist.javax.sip.LOG_FACTORY"); if (messageLogFactoryClasspath != null) { try { Class clazz = Class.forName(messageLogFactoryClasspath); Constructor c = clazz.getConstructor(new Class[0]); this.logRecordFactory = (LogRecordFactory) c .newInstance(new Object[0]); } catch (Exception ex) { if (isLoggingEnabled()) getStackLogger() .logError( "Bad configuration value for LOG_FACTORY -- using default logger"); this.logRecordFactory = new DefaultMessageLogFactory(); } } else { this.logRecordFactory = new DefaultMessageLogFactory(); } boolean computeContentLength = configurationProperties.getProperty( "gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "false").equalsIgnoreCase("true"); StringMsgParser .setComputeContentLengthFromMessage(computeContentLength); String tlsClientProtocols = configurationProperties.getProperty( "gov.nist.javax.sip.TLS_CLIENT_PROTOCOLS"); if (tlsClientProtocols != null) { StringTokenizer st = new StringTokenizer(tlsClientProtocols, " ,"); String[] protocols = new String[st.countTokens()]; int i=0; while (st.hasMoreTokens()) { protocols[i++] = st.nextToken(); } this.enabledProtocols = protocols; } super.rfc2543Supported = configurationProperties.getProperty( "gov.nist.javax.sip.RFC_2543_SUPPORT_ENABLED", "true") .equalsIgnoreCase("true"); super.cancelClientTransactionChecked = configurationProperties .getProperty( "gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true").equalsIgnoreCase("true"); super.logStackTraceOnMessageSend = configurationProperties.getProperty( "gov.nist.javax.sip.LOG_STACK_TRACE_ON_MESSAGE_SEND", "false") .equalsIgnoreCase("true"); if (isLoggingEnabled()) getStackLogger().logDebug( "created Sip stack. Properties = " + configurationProperties); InputStream in = getClass().getResourceAsStream("/TIMESTAMP"); if (in != null) { BufferedReader streamReader = new BufferedReader( new InputStreamReader(in)); try { String buildTimeStamp = streamReader.readLine(); if (in != null) { in.close(); } getStackLogger().setBuildTimeStamp(buildTimeStamp); } catch (IOException ex) { getStackLogger().logError("Could not open build timestamp."); } } String bufferSize = configurationProperties.getProperty( "gov.nist.javax.sip.RECEIVE_UDP_BUFFER_SIZE", MAX_DATAGRAM_SIZE .toString()); int bufferSizeInteger = new Integer(bufferSize).intValue(); super.setReceiveUdpBufferSize(bufferSizeInteger); bufferSize = configurationProperties.getProperty( "gov.nist.javax.sip.SEND_UDP_BUFFER_SIZE", MAX_DATAGRAM_SIZE .toString()); bufferSizeInteger = new Integer(bufferSize).intValue(); super.setSendUdpBufferSize(bufferSizeInteger); boolean congetstionControlEnabled = Boolean .parseBoolean(configurationProperties.getProperty( "gov.nist.javax.sip.CONGESTION_CONTROL_ENABLED", Boolean.TRUE.toString())); super.stackDoesCongestionControl = congetstionControlEnabled; super.isBackToBackUserAgent = Boolean .parseBoolean(configurationProperties.getProperty( "gov.nist.javax.sip.IS_BACK_TO_BACK_USER_AGENT", Boolean.FALSE.toString())); super.checkBranchId = Boolean.parseBoolean(configurationProperties .getProperty("gov.nist.javax.sip.REJECT_STRAY_RESPONSES", Boolean.FALSE.toString())); super.isDialogTerminatedEventDeliveredForNullDialog = (Boolean.parseBoolean(configurationProperties.getProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", Boolean.FALSE.toString()))); super.maxForkTime = Integer.parseInt( configurationProperties.getProperty("gov.nist.javax.sip.MAX_FORK_TIME_SECONDS","0")); } /* * (non-Javadoc) * * @see javax.sip.SipStack#createListeningPoint(java.lang.String, int, * java.lang.String) */ public synchronized ListeningPoint createListeningPoint(String address, int port, String transport) throws TransportNotSupportedException, InvalidArgumentException { if (isLoggingEnabled()) getStackLogger().logDebug( "createListeningPoint : address = " + address + " port = " + port + " transport = " + transport); if (address == null) throw new NullPointerException( "Address for listening point is null!"); if (transport == null) throw new NullPointerException("null transport"); if (port <= 0) throw new InvalidArgumentException("bad port"); if (!transport.equalsIgnoreCase("UDP") && !transport.equalsIgnoreCase("TLS") && !transport.equalsIgnoreCase("TCP") && !transport.equalsIgnoreCase("SCTP")) throw new TransportNotSupportedException("bad transport " + transport); /** Reusing an old stack instance */ if (!this.isAlive()) { this.toExit = false; this.reInitialize(); } String key = ListeningPointImpl.makeKey(address, port, transport); ListeningPointImpl lip = (ListeningPointImpl) listeningPoints.get(key); if (lip != null) { return lip; } else { try { InetAddress inetAddr = InetAddress.getByName(address); MessageProcessor messageProcessor = this .createMessageProcessor(inetAddr, port, transport); if (this.isLoggingEnabled()) { this.getStackLogger().logDebug( "Created Message Processor: " + address + " port = " + port + " transport = " + transport); } lip = new ListeningPointImpl(this, port, transport); lip.messageProcessor = messageProcessor; messageProcessor.setListeningPoint(lip); this.listeningPoints.put(key, lip); // start processing messages. messageProcessor.start(); return (ListeningPoint) lip; } catch (java.io.IOException ex) { if (isLoggingEnabled()) getStackLogger().logError( "Invalid argument address = " + address + " port = " + port + " transport = " + transport); throw new InvalidArgumentException(ex.getMessage(), ex); } } } /* * (non-Javadoc) * * @see javax.sip.SipStack#createSipProvider(javax.sip.ListeningPoint) */ public SipProvider createSipProvider(ListeningPoint listeningPoint) throws ObjectInUseException { if (listeningPoint == null) throw new NullPointerException("null listeningPoint"); if (this.isLoggingEnabled()) this.getStackLogger().logDebug( "createSipProvider: " + listeningPoint); ListeningPointImpl listeningPointImpl = (ListeningPointImpl) listeningPoint; if (listeningPointImpl.sipProvider != null) throw new ObjectInUseException("Provider already attached!"); SipProviderImpl provider = new SipProviderImpl(this); provider.setListeningPoint(listeningPointImpl); listeningPointImpl.sipProvider = provider; this.sipProviders.add(provider); return provider; } /* * (non-Javadoc) * * @see javax.sip.SipStack#deleteListeningPoint(javax.sip.ListeningPoint) */ public void deleteListeningPoint(ListeningPoint listeningPoint) throws ObjectInUseException { if (listeningPoint == null) throw new NullPointerException("null listeningPoint arg"); ListeningPointImpl lip = (ListeningPointImpl) listeningPoint; // Stop the message processing thread in the listening point. super.removeMessageProcessor(lip.messageProcessor); String key = lip.getKey(); this.listeningPoints.remove(key); } /* * (non-Javadoc) * * @see javax.sip.SipStack#deleteSipProvider(javax.sip.SipProvider) */ public void deleteSipProvider(SipProvider sipProvider) throws ObjectInUseException { if (sipProvider == null) throw new NullPointerException("null provider arg"); SipProviderImpl sipProviderImpl = (SipProviderImpl) sipProvider; // JvB: API doc is not clear, but in_use == // sipProviderImpl.sipListener!=null // so we should throw if app did not call removeSipListener // sipProviderImpl.sipListener = null; if (sipProviderImpl.getSipListener() != null) { throw new ObjectInUseException( "SipProvider still has an associated SipListener!"); } sipProviderImpl.removeListeningPoints(); // Bug reported by Rafael Barriuso sipProviderImpl.stop(); sipProviders.remove(sipProvider); if (sipProviders.isEmpty()) { this.stopStack(); } } /** * Get the IP Address of the stack. * * @see javax.sip.SipStack#getIPAddress() * @deprecated */ public String getIPAddress() { return super.getHostAddress(); } /* * (non-Javadoc) * * @see javax.sip.SipStack#getListeningPoints() */ public java.util.Iterator getListeningPoints() { return this.listeningPoints.values().iterator(); } /** * Return true if retransmission filter is active. * * @see javax.sip.SipStack#isRetransmissionFilterActive() * @deprecated */ public boolean isRetransmissionFilterActive() { return true; } /* * (non-Javadoc) * * @see javax.sip.SipStack#getSipProviders() */ public java.util.Iterator getSipProviders() { return this.sipProviders.iterator(); } /* * (non-Javadoc) * * @see javax.sip.SipStack#getStackName() */ public String getStackName() { return this.stackName; } /** * Finalization -- stop the stack on finalization. Exit the transaction * scanner and release all resources. * * @see java.lang.Object#finalize() */ protected void finalize() { this.stopStack(); } /** * This uses the default stack address to create a listening point. * * @see javax.sip.SipStack#createListeningPoint(java.lang.String, int, * java.lang.String) * @deprecated */ public ListeningPoint createListeningPoint(int port, String transport) throws TransportNotSupportedException, InvalidArgumentException { if (super.stackAddress == null) throw new NullPointerException( "Stack does not have a default IP Address!"); return this.createListeningPoint(super.stackAddress, port, transport); } /* * (non-Javadoc) * * @see javax.sip.SipStack#stop() */ public void stop() { if (isLoggingEnabled()) { getStackLogger().logDebug("stopStack -- stoppping the stack"); } this.stopStack(); this.sipProviders = new LinkedList(); this.listeningPoints = new Hashtable(); /* * Check for presence of an event scanner ( may happen if stack is * stopped before listener is attached ). */ if (this.eventScanner != null) this.eventScanner.forceStop(); this.eventScanner = null; } /* * (non-Javadoc) * * @see javax.sip.SipStack#start() */ public void start() throws ProviderDoesNotExistException, SipException { // Start a new event scanner if one does not exist. if (this.eventScanner == null) { this.eventScanner = new EventScanner(this); } } /** * Get the listener for the stack. A stack can have only one listener. To * get an event from a provider, the listener has to be registered with the * provider. The SipListener is application code. * * @return -- the stack SipListener * */ public SipListener getSipListener() { return this.sipListener; } /** * Get the message log factory registered with the stack. * * @return -- the messageLogFactory of the stack. */ public LogRecordFactory getLogRecordFactory() { return super.logRecordFactory; } /** * Set the log appender ( this is useful if you want to specify a particular * log format or log to something other than a file for example). This method * is will be removed May 11, 2010 or shortly there after. * * @param Appender * - the log4j appender to add. * @deprecated TODO: remove this method May 11, 2010. */ // BEGIN android-deleted /* @Deprecated public void addLogAppender(org.apache.log4j.Appender appender) { if (this.getStackLogger() instanceof gov.nist.core.LogWriter) { ((gov.nist.core.LogWriter) this.getStackLogger()).addAppender(appender); } } */ // END android-deleted /** * Get the log4j logger ( for log stream integration ). * This method will be removed May 11, 2010 or shortly there after. * * @return the log4j logger. * @deprecated TODO: This method will be removed May 11, 2010. */ @Deprecated // BEGIN andoird-deleted /* public org.apache.log4j.Logger getLogger() { if (this.getStackLogger() instanceof gov.nist.core.LogWriter) { return ((gov.nist.core.LogWriter) this.getStackLogger()).getLogger(); } return null; } */ // END android-deleted public EventScanner getEventScanner() { return eventScanner; } /* * (non-Javadoc) * * @see * gov.nist.javax.sip.SipStackExt#getAuthenticationHelper(gov.nist.javax * .sip.clientauthutils.AccountManager, javax.sip.header.HeaderFactory) */ public AuthenticationHelper getAuthenticationHelper( AccountManager accountManager, HeaderFactory headerFactory) { return new AuthenticationHelperImpl(this, accountManager, headerFactory); } /* * (non-Javadoc) * * @see * gov.nist.javax.sip.SipStackExt#getAuthenticationHelper(gov.nist.javax * .sip.clientauthutils.AccountManager, javax.sip.header.HeaderFactory) */ public AuthenticationHelper getSecureAuthenticationHelper( SecureAccountManager accountManager, HeaderFactory headerFactory) { return new AuthenticationHelperImpl(this, accountManager, headerFactory); } /** * Set the list of cipher suites supported by the stack. A stack can have * only one set of suites. These are not validated against the supported * cipher suites of the java runtime, so specifying a cipher here does not * guarantee that it will work.
    * The stack has a default cipher suite of: *
      *
    • TLS_RSA_WITH_AES_128_CBC_SHA
    • *
    • SSL_RSA_WITH_3DES_EDE_CBC_SHA
    • *
    • TLS_DH_anon_WITH_AES_128_CBC_SHA
    • *
    • SSL_DH_anon_WITH_3DES_EDE_CBC_SHA
    • *
    * * NOTE: This function must be called before adding a TLS listener * * @param String * [] The new set of ciphers to support. * @return * */ public void setEnabledCipherSuites(String[] newCipherSuites) { cipherSuites = newCipherSuites; } /** * Return the currently enabled cipher suites of the Stack. * * @return The currently enabled cipher suites. */ public String[] getEnabledCipherSuites() { return cipherSuites; } /** * Set the list of protocols supported by the stack for outgoing TLS connections. * A stack can have only one set of protocols. * These are not validated against the supported * protocols of the java runtime, so specifying a protocol here does not * guarantee that it will work.
    * The stack has a default protocol suite of: *
      *
    • SSLv3
    • *
    • SSLv2Hello
    • *
    • TLSv1
    • *
    * * NOTE: This function must be called before creating a TLSMessageChannel. * * @param String * [] The new set of protocols to use for outgoing TLS connections. * @return * */ public void setEnabledProtocols(String[] newProtocols) { enabledProtocols = newProtocols; } /** * Return the currently enabled protocols to use when creating TLS connection. * * @return The currently enabled protocols. */ public String[] getEnabledProtocols() { return enabledProtocols; } /** * Set the "back to back User Agent" flag. * * @param flag * - boolean flag to set. * */ public void setIsBackToBackUserAgent(boolean flag) { super.isBackToBackUserAgent = flag; } /** * Get the "back to back User Agent" flag. * * return the value of the flag * */ public boolean isBackToBackUserAgent() { return super.isBackToBackUserAgent; } public boolean isAutomaticDialogErrorHandlingEnabled() { return super.isAutomaticDialogErrorHandlingEnabled; } public boolean acquireSem() { try { return this.stackSemaphore.tryAcquire(10, TimeUnit.SECONDS); } catch ( InterruptedException ex) { return false; } } public void releaseSem() { this.stackSemaphore.release(); } }