/*
* 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.LogWriter;
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 gov.nist.javax.sip.stack.ServerLog;
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 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;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
/**
* 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):
* <ul>
*
* <li><b>gov.nist.javax.sip.TRACE_LEVEL = integer </b><br/> <b> Use of this property is still
* supported but deprecated. Please use gov.nist.javax.sip.STACK_LOGGER and
* gov.nist.javax.sip.SERVER_LOGGER for integration with logging frameworks and for custom
* formatting of log records. </b> This property is used by the built in log4j based logger. You
* can use the standard log4j level names here (i.e. ERROR, INFO, WARNING, OFF, DEBUG, TRACE) If
* this is set to INFO or above, then incoming valid messages are logged in SERVER_LOG. If you set
* this to 32 and specify a DEBUG_LOG then vast amounts of trace information will be dumped in to
* the specified DEBUG_LOG. The server log accumulates the signaling trace. <a href="{@docRoot}/tools/tracesviewer/tracesviewer.html">
* This can be viewed using the trace viewer tool .</a> Please send us both the server log and
* debug log when reporting non-obvious problems. You can also use the strings DEBUG or INFO for
* level 32 and 16 respectively. If the value of this property is set to LOG4J, then the effective
* log levels are determined from the log4j settings file (e.g. log4j.properties). The logger name
* for the stack is specified using the gov.nist.javax.sip.LOG4J_LOGGER_NAME property. By default
* log4j logger name for the stack is the same as the stack name. For example, <code>
* properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "LOG4J");
* properties.setProperty("gov.nist.javax.sip.LOG4J_LOGGER_NAME", "SIPStackLogger");
* </code>
* allows you to now control logging in the stack entirely using log4j facilities. </li>
*
* <li><b>gov.nist.javax.sip.LOG_FACTORY = classpath </b> <b> Use of this property is still
* supported but deprecated. Please use gov.nist.javax.sip.STACK_LOGGER and
* gov.nist.javax.sip.SERVER_LOGGER for integration with logging frameworks and for custom
* formatting of log records. </b> <br/> The fully qualified classpath for an implementation of
* the MessageLogFactory. The stack calls the MessageLogFactory functions to format the log for
* messages that are received or sent. This function allows you to log auxiliary information
* related to the application or environmental conditions into the log stream. The log factory
* must have a default constructor. </li>
*
* <li><b>gov.nist.javax.sip.SERVER_LOG = fileName </b><br/> <b> Use of this property is still
* supported but deprecated. Please use gov.nist.javax.sip.STACK_LOGGER and
* gov.nist.javax.sip.SERVER_LOGGER for integration with logging frameworks and for custom
* formatting of log records. </b> Log valid incoming messages here. If this is left null AND the
* TRACE_LEVEL is above INFO (or TRACE) then the messages are printed to stdout. Otherwise
* messages are logged in a format that can later be viewed using the trace viewer application
* which is located in the tools/tracesviewer directory. <font color=red> Mail this to us with bug
* reports. </font> </li>
*
* <li><b>gov.nist.javax.sip.DEBUG_LOG = fileName </b> <b> Use of this property is still
* supported but deprecated. Please use gov.nist.javax.sip.STACK_LOGGER and
* gov.nist.javax.sip.SERVER_LOGGER for integration with logging frameworks and for custom
* formatting of log records. </b> <br/> Where the debug log goes. <font color=red> Mail this to
* us with bug reports. </font> </li>
*
* <li><b>gov.nist.javax.sip.LOG_MESSAGE_CONTENT = true|false </b><br/> Set true if you want to
* capture content into the log. Default is false. A bad idea to log content if you are using SIP
* to push a lot of bytes through TCP. </li>
*
* <li><b>gov.nist.javax.sip.LOG_STACK_TRACE_ON_MESSAGE_SEND = true|false </b><br/> Set true if
* you want to to log a stack trace at INFO level for each message send. This is really handy for
* debugging.</li>
*
* <li><b>gov.nist.javax.sip.STACK_LOGGER = full path name to the class implementing
* gov.nist.core.StackLogger interface</b><br/> If this property is defined the sip stack will
* try to instantiate it through a no arg constructor. This allows to use different logging
* implementations than the ones provided by default to log what happens within the stack while
* processing SIP messages. If this property is not defined, the default sip stack LogWriter will
* be used for logging</li>
*
* <li><b>gov.nist.javax.sip.SERVER_LOGGER = full path name to the class implementing
* gov.nist.core.ServerLogger interface</b><br/> If this property is defined the sip stack will
* try to instantiate it through a no arg constructor. This allows to use different logging
* implementations than the ones provided by default to log sent/received messages by the sip
* stack. If this property is not defined, the default sip stack ServerLog will be used for
* logging</li>
*
* <li><b>gov.nist.javax.sip.MAX_MESSAGE_SIZE = integer</b> <br/> 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. </li>
*
* <li><b>gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS = [true|false] </b> <br/> 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. </li>
*
* <li><b>gov.nist.javax.sip.CACHE_CLIENT_CONNECTIONS = [true|false] </b> <br/> 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. </li>
*
* <li> <b>gov.nist.javax.sip.THREAD_POOL_SIZE = integer </b> <br/> 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.
* <ul>
* <li>
* <li> If this is not specified, <b> and the listener is re-entrant</b>, each event delivered to
* the listener is run in the context of a new thread. </li>
* <li>If this is specified and the listener is re-entrant, then the stack will run the listener
* using a thread from the thread pool. This allows you to manage the level of concurrency to a
* fixed maximum. Threads are pre-allocated when the stack is instantiated.</li>
* <li> If this is specified and the listener is not re-entrant, then the stack will use the
* thread pool thread from this pool to parse and manage the state machine but will run the
* listener in its own thread. </li>
* </ul>
*
* <li> <b>gov.nist.javax.sip.REENTRANT_LISTENER = true|false </b> <br/> 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. <b> If you want good performance on a multithreaded machine write your listener to be
* re-entrant and set this property to be true </b></li>
*
* <li> <b>gov.nist.javax.sip.MAX_CONNECTIONS = integer </b> <br/> Max number of simultaneous TCP
* connections handled by stack. </li>
*
* <li><b>gov.nist.javax.sip.MAX_SERVER_TRANSACTIONS = integer </b> <br/> 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 </li>
*
* <li><b>gov.nist.javax.sip.MAX_CLIENT_TRANSACTIONS = integer </b> <br/> 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).
* </li>
*
* <li><b>gov.nist.javax.sip.PASS_INVITE_NON_2XX_ACK_TO_LISTENER = true|false </b> <br/> 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. </li>
*
* <li><b>gov.nist.javax.sip.MAX_LISTENER_RESPONSE_TIME = Integer </b> <br/>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.
*
* <li><b>gov.nist.javax.sip.USE_TLS_ACCELERATOR = true|false </b> <br/>Default value is false.
* Setting this to true permits the delegation of TLS encryption/decryption to an external, non
* SIP-aware, TLS accelerator hardware. Such deployment requires the SIP stack to make its TLS
* traffic goes over un-encrypted TCP connections to the TLS accelerator. So all TLS listening
* points will be listening for plain TCP traffic, and outgoing messages sent with a TLS provider
* will not be encrypted. Note that this does not affect the transport value in the Via header.
* Another deployment strategy for TLS acceleration would be to use one or a cluster of outbound
* proxies that transform the TCP or UDP SIP connection to TLS connections. This scenario does not
* need the USE_TLS_ACCELERATOR switch, as the messages will be sent using a plain TCP or UDP
* provider. </li>
*
* <li><b>gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_ACK = [true|false]</b> <br/>Default is
* <it>false</it>. 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 <it>true</it>. </li>
*
* <li> <b>gov.nist.javax.sip.READ_TIMEOUT = integer </b> <br/> 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. </li>
*
* <li> <b>gov.nist.javax.sip.NETWORK_LAYER = classpath </b> <br/> 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). </li>
*
* <li><b>gov.nist.javax.sip.ADDRESS_RESOLVER = classpath </b><br/> 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. </li>
*
* <li><b>gov.nist.javax.sip.AUTO_GENERATE_TIMESTAMP= [true| false] </b><br/> (default is false)
* Automatically generate a getTimeOfDay timestamp for a retransmitted request if the original
* request contained a timestamp. This is useful for profiling. </li>
*
* <li> <b>gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS = long </b> <br/> 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. </li>
*
*
*
* <li><b>gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY = [true|false] </b> <br/>
* Default is <it>false</it> If set to <it>true</it>, when you are creating a message from a
* <it>String</it>, 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.
*
* <li><b>gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED = [true|false] </b> <br/> Default
* is <it>true</it>. 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 <it>false</it> 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. </li>
*
* <li><b>gov.nist.javax.sip.IS_BACK_TO_BACK_USER_AGENT = [true|false] </b> <br/> Default is
* <it>false</it> This property controls a setting on the Dialog objects that the stack manages.
* Pure B2BUA applications should set this flag to <it>true</it>. This property can also be set
* on a per-dialog basis. Setting this to <it>true</it> 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. </li>
*
*
* <li><b>gov.nist.javax.sip.RECEIVE_UDP_BUFFER_SIZE = int </b> <br/> Default is <it>8*1024</it>.
* 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. </li>
*
* <li><b>gov.nist.javax.sip.SEND_UDP_BUFFER_SIZE = int </b> <br/> Default is <it>8*1024</it>.
* 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. </li>
*
* <li><b>gov.nist.javax.sip.CONGESTION_CONTROL_ENABLED = boolean </b> 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. </li>
*
* <li><b>gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY = [true|false] </b> <br/> Default is
* <it>false</it>. This flag is added to allow Sip Listeners to receive all NOTIFY requests
* including those that are not part of a valid dialog. </li>
*
* <li><b>gov.nist.javax.sip.REJECT_STRAY_RESPONSES = [true|false] </b> Default is <it>false</it>
* 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. </li>
*
* <li><b>javax.net.ssl.keyStore = fileName </b> <br/> Default is <it>NULL</it>. 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: <br>
* <code>
* properties.setProperty("javax.net.ssl.keyStorePassword", "<password>");
* </code>
* <br>
* The trust store can be changed, to a separate file with the following setting: <br>
* <code>
* properties.setProperty("javax.net.ssl.trustStore", "<trustStoreFileName location>");
* </code>
* <br>
* If the trust store property is provided the password on the trust store must be the same as the
* key store. <br>
* <br>
* <b> 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. </b>
*
*
* @version 1.2 $Revision: 1.100 $ $Date: 2009/11/04 20:37:55 $
*
* @author M. Ranganathan <br/>
*
*
*
*
*/
public class SipStackImpl extends SIPTransactionStack implements javax.sip.SipStack, SipStackExt {
private EventScanner eventScanner;
private Hashtable<String, ListeningPointImpl> listeningPoints;
private LinkedList<SipProviderImpl> 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;
// 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",
};
/**
* 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<String, ListeningPointImpl>();
this.sipProviders = new LinkedList<SipProviderImpl>();
}
/**
* ReInitialize the stack instance.
*/
private void reInitialize() {
super.reInit();
this.eventScanner = new EventScanner(this);
this.listeningPoints = new Hashtable<String, ListeningPointImpl>();
this.sipProviders = new LinkedList<SipProviderImpl>();
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) {
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);
}
} else {
this.setStackLogger(new LogWriter(configurationProperties));
}
String serverLoggerClassName = configurationProperties
.getProperty("gov.nist.javax.sip.SERVER_LOGGER");
// To log debug messages.
if (serverLoggerClassName != null) {
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);
}
} else {
this.serverLogger = new ServerLog(this, configurationProperties);
}
// 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.looseDialogValidation = configurationProperties.getProperty(
"gov.nist.javax.sip.LOOSE_DIALOG_VALIDATION", "false").equalsIgnoreCase("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.useTlsAccelerator = false;
String useTlsAcceleratorFlag = configurationProperties
.getProperty("gov.nist.javax.sip.USE_TLS_ACCELERATOR");
if (useTlsAcceleratorFlag != null
&& "true".equalsIgnoreCase(useTlsAcceleratorFlag.trim())) {
this.useTlsAccelerator = true;
}
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) {
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) {
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) {
this.getStackLogger().logError(
"transaction table size - bad value " + ex.getMessage());
}
}
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) {
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.
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) {
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) {
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) {
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);
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");
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()));
}
/*
* (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 {
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) {
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.getStackLogger().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.sipListener != 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<SipProviderImpl> 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 (getStackLogger().isLoggingEnabled()) {
getStackLogger().logDebug("stopStack -- stoppping the stack");
}
this.stopStack();
this.sipProviders = new LinkedList<SipProviderImpl>();
this.listeningPoints = new Hashtable<String, ListeningPointImpl>();
/*
* 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
*
*/
protected 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).
*
* @param Appender - the log4j appender to add.
* @deprecated
*/
public void addLogAppender(Appender appender) {
if (this.getStackLogger() instanceof LogWriter) {
((LogWriter) this.getStackLogger()).addAppender(appender);
}
}
/**
* Get the log4j logger ( for log stream integration ).
*
* @return
* @deprecated
*/
public Logger getLogger() {
if (this.getStackLogger() instanceof LogWriter) {
return ((LogWriter) this.getStackLogger()).getLogger();
}
return null;
}
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.<br>
* The stack has a default cipher suite of:
* <ul>
* <li> TLS_RSA_WITH_AES_128_CBC_SHA </li>
* <li> SSL_RSA_WITH_3DES_EDE_CBC_SHA </li>
* <li> TLS_DH_anon_WITH_AES_128_CBC_SHA </li>
* <li> SSL_DH_anon_WITH_3DES_EDE_CBC_SHA </li>
* </ul>
*
* <b>NOTE: This function must be called before adding a TLS listener</b>
*
* @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 "back to back User Agent" flag.
*
* @param flag - boolean flag to set.
*
*/
public void setIsBackToBackUserAgent(boolean flag) {
super.isBackToBackUserAgent = flag;
}
}