739 lines
29 KiB
Java
739 lines
29 KiB
Java
/*
|
|
* 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
|
|
*
|
|
* .
|
|
*
|
|
*/
|
|
/* This class is entirely derived from TCPMessageChannel,
|
|
* by making some minor changes. Daniel J. Martinez Manzano <dani@dif.um.es>
|
|
* made these changes. Ahmet Uyar
|
|
* <auyar@csit.fsu.edu>sent in a bug report for TCP operation of the
|
|
* JAIN sipStack. Niklas Uhrberg suggested that a mechanism be added to
|
|
* limit the number of simultaneous open connections. The TLS
|
|
* Adaptations were contributed by Daniel Martinez. Hagai Sela
|
|
* contributed a bug fix for symmetric nat. Jeroen van Bemmel
|
|
* added compensation for buggy clients ( Microsoft RTC clients ).
|
|
* Bug fixes by viswashanti.kadiyala@antepo.com, Joost Yervante Damand
|
|
* Lamine Brahimi (IBM Zurich) sent in a bug fix - a thread was being uncessarily created.
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* Product of NIST/ITL Advanced Networking Technologies Division (ANTD). *
|
|
******************************************************************************/
|
|
package gov.nist.javax.sip.stack;
|
|
|
|
import gov.nist.javax.sip.header.*;
|
|
import gov.nist.javax.sip.message.*;
|
|
import gov.nist.javax.sip.parser.*;
|
|
import gov.nist.core.*;
|
|
import java.net.*;
|
|
import java.io.*;
|
|
import java.text.ParseException;
|
|
|
|
import javax.net.ssl.HandshakeCompletedListener;
|
|
import javax.net.ssl.SSLSocket;
|
|
import javax.sip.address.Hop;
|
|
import javax.sip.message.Response;
|
|
|
|
/**
|
|
* This is sipStack for TLS connections. This abstracts a stream of parsed messages. The SIP
|
|
* sipStack starts this from the main SIPStack class for each connection that it accepts. It
|
|
* starts a message parser in its own thread and talks to the message parser via a pipe. The
|
|
* message parser calls back via the parseError or processMessage functions that are defined as
|
|
* part of the SIPMessageListener interface.
|
|
*
|
|
* @see gov.nist.javax.sip.parser.PipelinedMsgParser
|
|
*
|
|
*
|
|
* @author M. Ranganathan
|
|
*
|
|
*
|
|
* @version 1.2 $Revision: 1.27 $ $Date: 2010/01/10 00:13:14 $
|
|
*/
|
|
public final class TLSMessageChannel extends MessageChannel implements SIPMessageListener,
|
|
Runnable, RawMessageChannel {
|
|
|
|
private Socket mySock;
|
|
|
|
private PipelinedMsgParser myParser;
|
|
|
|
private InputStream myClientInputStream; // just to pass to thread.
|
|
|
|
private String key;
|
|
|
|
protected boolean isCached;
|
|
|
|
protected boolean isRunning;
|
|
|
|
private Thread mythread;
|
|
|
|
private String myAddress;
|
|
|
|
private int myPort;
|
|
|
|
private InetAddress peerAddress;
|
|
|
|
private int peerPort;
|
|
|
|
private String peerProtocol;
|
|
|
|
// Incremented whenever a transaction gets assigned
|
|
// to the message channel and decremented when
|
|
// a transaction gets freed from the message channel.
|
|
// protected int useCount = 0;
|
|
|
|
private TLSMessageProcessor tlsMessageProcessor;
|
|
|
|
private SIPTransactionStack sipStack;
|
|
|
|
private HandshakeCompletedListener handshakeCompletedListener;
|
|
|
|
/**
|
|
* Constructor - gets called from the SIPStack class with a socket on accepting a new client.
|
|
* All the processing of the message is done here with the sipStack being freed up to handle
|
|
* new connections. The sock input is the socket that is returned from the accept. Global data
|
|
* that is shared by all threads is accessible in the Server structure.
|
|
*
|
|
* @param sock Socket from which to read and write messages. The socket is already connected
|
|
* (was created as a result of an accept).
|
|
*
|
|
* @param sipStack Ptr to SIP Stack
|
|
*
|
|
* @param msgProcessor -- the message processor that created us.
|
|
*/
|
|
|
|
protected TLSMessageChannel(Socket sock, SIPTransactionStack sipStack,
|
|
TLSMessageProcessor msgProcessor) throws IOException {
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logDebug("creating new TLSMessageChannel (incoming)");
|
|
sipStack.getStackLogger().logStackTrace();
|
|
}
|
|
|
|
mySock = (SSLSocket) sock;
|
|
if ( sock instanceof SSLSocket ) {
|
|
|
|
SSLSocket sslSock = (SSLSocket) sock;
|
|
sslSock.setNeedClientAuth(true);
|
|
this.handshakeCompletedListener = new HandshakeCompletedListenerImpl(this);
|
|
sslSock.addHandshakeCompletedListener(this.handshakeCompletedListener);
|
|
sslSock.startHandshake();
|
|
|
|
}
|
|
|
|
peerAddress = mySock.getInetAddress();
|
|
myAddress = msgProcessor.getIpAddress().getHostAddress();
|
|
myClientInputStream = mySock.getInputStream();
|
|
|
|
mythread = new Thread(this);
|
|
mythread.setDaemon(true);
|
|
mythread.setName("TLSMessageChannelThread");
|
|
// Stash away a pointer to our sipStack structure.
|
|
this.sipStack = sipStack;
|
|
|
|
this.tlsMessageProcessor = msgProcessor;
|
|
this.myPort = this.tlsMessageProcessor.getPort();
|
|
this.peerPort = mySock.getPort();
|
|
// Bug report by Vishwashanti Raj Kadiayl
|
|
super.messageProcessor = msgProcessor;
|
|
// Can drop this after response is sent potentially.
|
|
mythread.start();
|
|
}
|
|
|
|
/**
|
|
* Constructor - connects to the given inet address.
|
|
*
|
|
* @param inetAddr inet address to connect to.
|
|
* @param sipStack is the sip sipStack from which we are created.
|
|
* @param messageProcessor -- the message processor that created us.
|
|
* @throws IOException if we cannot connect.
|
|
*/
|
|
protected TLSMessageChannel(InetAddress inetAddr, int port, SIPTransactionStack sipStack,
|
|
TLSMessageProcessor messageProcessor) throws IOException {
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logDebug("creating new TLSMessageChannel (outgoing)");
|
|
sipStack.getStackLogger().logStackTrace();
|
|
}
|
|
this.peerAddress = inetAddr;
|
|
this.peerPort = port;
|
|
this.myPort = messageProcessor.getPort();
|
|
this.peerProtocol = "TLS";
|
|
this.sipStack = sipStack;
|
|
this.tlsMessageProcessor = messageProcessor;
|
|
this.myAddress = messageProcessor.getIpAddress().getHostAddress();
|
|
this.key = MessageChannel.getKey(peerAddress, peerPort, "TLS");
|
|
super.messageProcessor = messageProcessor;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns "true" as this is a reliable transport.
|
|
*/
|
|
public boolean isReliable() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Close the message channel.
|
|
*/
|
|
public void close() {
|
|
try {
|
|
if (mySock != null)
|
|
mySock.close();
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger().logDebug("Closing message Channel " + this);
|
|
} catch (IOException ex) {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger().logDebug("Error closing socket " + ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get my SIP Stack.
|
|
*
|
|
* @return The SIP Stack for this message channel.
|
|
*/
|
|
public SIPTransactionStack getSIPStack() {
|
|
return sipStack;
|
|
}
|
|
|
|
/**
|
|
* get the transport string.
|
|
*
|
|
* @return "tcp" in this case.
|
|
*/
|
|
public String getTransport() {
|
|
return "tls";
|
|
}
|
|
|
|
/**
|
|
* get the address of the client that sent the data to us.
|
|
*
|
|
* @return Address of the client that sent us data that resulted in this channel being
|
|
* created.
|
|
*/
|
|
public String getPeerAddress() {
|
|
if (peerAddress != null) {
|
|
return peerAddress.getHostAddress();
|
|
} else
|
|
return getHost();
|
|
}
|
|
|
|
protected InetAddress getPeerInetAddress() {
|
|
return peerAddress;
|
|
}
|
|
|
|
public String getPeerProtocol() {
|
|
return this.peerProtocol;
|
|
}
|
|
|
|
/**
|
|
* Send message to whoever is connected to us. Uses the topmost via address to send to.
|
|
*
|
|
* @param msg is the message to send.
|
|
* @param retry
|
|
*/
|
|
private void sendMessage(byte[] msg, boolean retry) throws IOException {
|
|
Socket sock = this.sipStack.ioHandler.sendBytes(
|
|
this.getMessageProcessor().getIpAddress(), this.peerAddress, this.peerPort,
|
|
this.peerProtocol, msg, retry,this);
|
|
// Created a new socket so close the old one and stick the new
|
|
// one in its place but dont do this if it is a datagram socket.
|
|
// (could have replied via udp but received via tcp!).
|
|
if (sock != mySock && sock != null) {
|
|
try {
|
|
if (mySock != null)
|
|
mySock.close();
|
|
} catch (IOException ex) {
|
|
}
|
|
mySock = sock;
|
|
this.myClientInputStream = mySock.getInputStream();
|
|
|
|
Thread thread = new Thread(this);
|
|
thread.setDaemon(true);
|
|
thread.setName("TLSMessageChannelThread");
|
|
thread.start();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Return a formatted message to the client. We try to re-connect with the peer on the other
|
|
* end if possible.
|
|
*
|
|
* @param sipMessage Message to send.
|
|
* @throws IOException If there is an error sending the message
|
|
*/
|
|
public void sendMessage(SIPMessage sipMessage) throws IOException {
|
|
byte[] msg = sipMessage.encodeAsBytes(this.getTransport());
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
this.sendMessage(msg, sipMessage instanceof SIPRequest);
|
|
|
|
if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES))
|
|
logMessage(sipMessage, peerAddress, peerPort, time);
|
|
}
|
|
|
|
/**
|
|
* Send a message to a specified address.
|
|
*
|
|
* @param message Pre-formatted message to send.
|
|
* @param receiverAddress Address to send it to.
|
|
* @param receiverPort Receiver port.
|
|
* @throws IOException If there is a problem connecting or sending.
|
|
*/
|
|
public void sendMessage(byte message[], InetAddress receiverAddress, int receiverPort,
|
|
boolean retry) throws IOException {
|
|
if (message == null || receiverAddress == null)
|
|
throw new IllegalArgumentException("Null argument");
|
|
Socket sock = this.sipStack.ioHandler.sendBytes(this.messageProcessor.getIpAddress(),
|
|
receiverAddress, receiverPort, "TLS", message, retry, this);
|
|
//
|
|
// Created a new socket so close the old one and s
|
|
// Check for null (bug fix sent in by Christophe)
|
|
if (sock != mySock && sock != null) {
|
|
try {
|
|
if (mySock != null)
|
|
mySock.close();
|
|
} catch (IOException ex) {
|
|
/* ignore */
|
|
}
|
|
mySock = sock;
|
|
this.myClientInputStream = mySock.getInputStream();
|
|
|
|
// start a new reader on this end of the pipe.
|
|
Thread mythread = new Thread(this);
|
|
mythread.setDaemon(true);
|
|
mythread.setName("TLSMessageChannelThread");
|
|
mythread.start();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Exception processor for exceptions detected from the parser. (This is invoked by the parser
|
|
* when an error is detected).
|
|
*
|
|
* @param sipMessage -- the message that incurred the error.
|
|
* @param ex -- parse exception detected by the parser.
|
|
* @param header -- header that caused the error.
|
|
* @throws ParseException Thrown if we want to reject the message.
|
|
*/
|
|
public void handleException(ParseException ex, SIPMessage sipMessage, Class hdrClass,
|
|
String header, String message) throws ParseException {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger().logException(ex);
|
|
// Log the bad message for later reference.
|
|
if ((hdrClass != null)
|
|
&& (hdrClass.equals(From.class) || hdrClass.equals(To.class)
|
|
|| hdrClass.equals(CSeq.class) || hdrClass.equals(Via.class)
|
|
|| hdrClass.equals(CallID.class) || hdrClass.equals(RequestLine.class) || hdrClass
|
|
.equals(StatusLine.class))) {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger().logDebug("Encountered bad message \n" + message);
|
|
// JvB: send a 400 response for requests (except ACK)
|
|
String msgString = sipMessage.toString();
|
|
if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {
|
|
|
|
String badReqRes = createBadReqRes(msgString, ex);
|
|
if (badReqRes != null) {
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logDebug("Sending automatic 400 Bad Request:");
|
|
sipStack.getStackLogger().logDebug(badReqRes);
|
|
}
|
|
try {
|
|
this.sendMessage(badReqRes.getBytes(), this.getPeerInetAddress(), this
|
|
.getPeerPort(), false);
|
|
} catch (IOException e) {
|
|
this.sipStack.getStackLogger().logException(e);
|
|
}
|
|
} else {
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logDebug(
|
|
"Could not formulate automatic 400 Bad Request");
|
|
}
|
|
}
|
|
}
|
|
throw ex;
|
|
} else {
|
|
sipMessage.addUnparsed(header);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets invoked by the parser as a callback on successful message parsing (i.e. no parser
|
|
* errors).
|
|
*
|
|
* @param sipMessage Message to process (this calls the application for processing the
|
|
* message).
|
|
*
|
|
* Jvb: note that this code is identical to TCPMessageChannel, refactor some day
|
|
*/
|
|
public void processMessage(SIPMessage sipMessage) throws Exception {
|
|
try {
|
|
if (sipMessage.getFrom() == null || sipMessage.getTo() == null
|
|
|| sipMessage.getCallId() == null || sipMessage.getCSeq() == null
|
|
|| sipMessage.getViaHeaders() == null) {
|
|
String badmsg = sipMessage.encode();
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logError("bad message " + badmsg);
|
|
sipStack.getStackLogger().logError(">>> Dropped Bad Msg");
|
|
}
|
|
return;
|
|
}
|
|
|
|
ViaList viaList = sipMessage.getViaHeaders();
|
|
// For a request
|
|
// first via header tells where the message is coming from.
|
|
// For response, this has already been recorded in the outgoing
|
|
// message.
|
|
|
|
if (sipMessage instanceof SIPRequest) {
|
|
Via v = (Via) viaList.getFirst();
|
|
// the peer address and tag it appropriately.
|
|
Hop hop = sipStack.addressResolver.resolveAddress(v.getHop());
|
|
this.peerProtocol = v.getTransport();
|
|
try {
|
|
this.peerAddress = mySock.getInetAddress();
|
|
// Check to see if the received parameter matches
|
|
// JvB: dont do this. It is both costly and incorrect
|
|
// Must set received also when it is a FQDN, regardless whether
|
|
// it resolves to the correct IP address
|
|
// InetAddress sentByAddress = InetAddress.getByName(hop.getHost());
|
|
// JvB: if sender added 'rport', must always set received
|
|
if (v.hasParameter(Via.RPORT)
|
|
|| !hop.getHost().equals(this.peerAddress.getHostAddress())) {
|
|
v.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress());
|
|
}
|
|
// @@@ hagai
|
|
// JvB: technically, may only do this when Via already contains
|
|
// rport
|
|
v.setParameter(Via.RPORT, Integer.toString(this.peerPort));
|
|
} catch (java.text.ParseException ex) {
|
|
InternalErrorHandler.handleException(ex);
|
|
}
|
|
// Use this for outgoing messages as well.
|
|
if (!this.isCached) {
|
|
((TLSMessageProcessor) this.messageProcessor).cacheMessageChannel(this);
|
|
this.isCached = true;
|
|
String key = IOHandler.makeKey(mySock.getInetAddress(), this.peerPort);
|
|
sipStack.ioHandler.putSocket(key, mySock);
|
|
}
|
|
}
|
|
|
|
// Foreach part of the request header, fetch it and process it
|
|
|
|
long receptionTime = System.currentTimeMillis();
|
|
//
|
|
|
|
if (sipMessage instanceof SIPRequest) {
|
|
// This is a request - process the request.
|
|
SIPRequest sipRequest = (SIPRequest) sipMessage;
|
|
// Create a new sever side request processor for this
|
|
// message and let it handle the rest.
|
|
|
|
if (sipStack.isLoggingEnabled()) {
|
|
sipStack.getStackLogger().logDebug("----Processing Message---");
|
|
}
|
|
if (this.sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) {
|
|
|
|
sipStack.serverLogger.logMessage(sipMessage, this.getPeerHostPort().toString(),
|
|
this.messageProcessor.getIpAddress().getHostAddress() + ":"
|
|
+ this.messageProcessor.getPort(), false, receptionTime);
|
|
|
|
}
|
|
// Check for reasonable size - reject message
|
|
// if it is too long.
|
|
if (sipStack.getMaxMessageSize() > 0
|
|
&& sipRequest.getSize()
|
|
+ (sipRequest.getContentLength() == null ? 0 : sipRequest
|
|
.getContentLength().getContentLength()) > sipStack
|
|
.getMaxMessageSize()) {
|
|
SIPResponse sipResponse = sipRequest
|
|
.createResponse(SIPResponse.MESSAGE_TOO_LARGE);
|
|
byte[] resp = sipResponse.encodeAsBytes(this.getTransport());
|
|
this.sendMessage(resp, false);
|
|
throw new Exception("Message size exceeded");
|
|
}
|
|
|
|
// Stack could not create a new server request interface.
|
|
// maybe not enough resources.
|
|
ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest(
|
|
sipRequest, this);
|
|
if (sipServerRequest != null) {
|
|
try {
|
|
sipServerRequest.processRequest(sipRequest, this);
|
|
} finally {
|
|
if (sipServerRequest instanceof SIPTransaction) {
|
|
SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest;
|
|
if (!sipServerTx.passToListener())
|
|
((SIPTransaction) sipServerRequest).releaseSem();
|
|
}
|
|
}
|
|
} else {
|
|
SIPResponse response = sipRequest
|
|
.createResponse(Response.SERVICE_UNAVAILABLE);
|
|
|
|
RetryAfter retryAfter = new RetryAfter();
|
|
|
|
// Be a good citizen and send a decent response code back.
|
|
try {
|
|
retryAfter.setRetryAfter((int) (10 * (Math.random())));
|
|
response.setHeader(retryAfter);
|
|
this.sendMessage(response);
|
|
} catch (Exception e) {
|
|
// IGNore
|
|
}
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger()
|
|
.logWarning("Dropping message -- could not acquire semaphore");
|
|
}
|
|
} else {
|
|
SIPResponse sipResponse = (SIPResponse) sipMessage;
|
|
try {
|
|
sipResponse.checkHeaders();
|
|
} catch (ParseException ex) {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger()
|
|
.logError("Dropping Badly formatted response message >>> "
|
|
+ sipResponse);
|
|
return;
|
|
}
|
|
// This is a response message - process it.
|
|
// Check the size of the response.
|
|
// If it is too large dump it silently.
|
|
if (sipStack.getMaxMessageSize() > 0
|
|
&& sipResponse.getSize()
|
|
+ (sipResponse.getContentLength() == null ? 0 : sipResponse
|
|
.getContentLength().getContentLength()) > sipStack
|
|
.getMaxMessageSize()) {
|
|
if (sipStack.isLoggingEnabled())
|
|
this.sipStack.getStackLogger().logDebug("Message size exceeded");
|
|
return;
|
|
|
|
}
|
|
ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse(
|
|
sipResponse, this);
|
|
if (sipServerResponse != null) {
|
|
try {
|
|
if (sipServerResponse instanceof SIPClientTransaction
|
|
&& !((SIPClientTransaction) sipServerResponse)
|
|
.checkFromTag(sipResponse)) {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger()
|
|
.logError("Dropping response message with invalid tag >>> "
|
|
+ sipResponse);
|
|
return;
|
|
}
|
|
|
|
sipServerResponse.processResponse(sipResponse, this);
|
|
} finally {
|
|
if (sipServerResponse instanceof SIPTransaction
|
|
&& !((SIPTransaction) sipServerResponse).passToListener()) {
|
|
// Note that the semaphore is released in event
|
|
// scanner if the
|
|
// request is actually processed by the Listener.
|
|
((SIPTransaction) sipServerResponse).releaseSem();
|
|
}
|
|
}
|
|
} else {
|
|
sipStack.getStackLogger().logWarning("Could not get semaphore... dropping response");
|
|
}
|
|
}
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This gets invoked when thread.start is called from the constructor. Implements a message
|
|
* loop - reading the tcp connection and processing messages until we are done or the other
|
|
* end has closed.
|
|
*/
|
|
public void run() {
|
|
Pipeline hispipe = null;
|
|
// Create a pipeline to connect to our message parser.
|
|
hispipe = new Pipeline(myClientInputStream, sipStack.readTimeout,
|
|
((SIPTransactionStack) sipStack).getTimer());
|
|
// Create a pipelined message parser to read and parse
|
|
// messages that we write out to him.
|
|
myParser = new PipelinedMsgParser(this, hispipe, this.sipStack.getMaxMessageSize());
|
|
// Start running the parser thread.
|
|
myParser.processInput();
|
|
// bug fix by Emmanuel Proulx
|
|
int bufferSize = 4096;
|
|
this.tlsMessageProcessor.useCount++;
|
|
this.isRunning = true;
|
|
try {
|
|
while (true) {
|
|
try {
|
|
byte[] msg = new byte[bufferSize];
|
|
int nbytes = myClientInputStream.read(msg, 0, bufferSize);
|
|
// no more bytes to read...
|
|
if (nbytes == -1) {
|
|
hispipe.write("\r\n\r\n".getBytes("UTF-8"));
|
|
try {
|
|
if (sipStack.maxConnections != -1) {
|
|
synchronized (tlsMessageProcessor) {
|
|
tlsMessageProcessor.nConnections--;
|
|
tlsMessageProcessor.notify();
|
|
}
|
|
}
|
|
hispipe.close();
|
|
mySock.close();
|
|
} catch (IOException ioex) {
|
|
}
|
|
return;
|
|
}
|
|
hispipe.write(msg, 0, nbytes);
|
|
|
|
} catch (IOException ex) {
|
|
// Terminate the message.
|
|
try {
|
|
hispipe.write("\r\n\r\n".getBytes("UTF-8"));
|
|
} catch (Exception e) {
|
|
// InternalErrorHandler.handleException(e);
|
|
}
|
|
|
|
try {
|
|
if (sipStack.isLoggingEnabled())
|
|
sipStack.getStackLogger().logDebug("IOException closing sock " + ex);
|
|
try {
|
|
if (sipStack.maxConnections != -1) {
|
|
synchronized (tlsMessageProcessor) {
|
|
tlsMessageProcessor.nConnections--;
|
|
tlsMessageProcessor.notify();
|
|
}
|
|
}
|
|
mySock.close();
|
|
hispipe.close();
|
|
} catch (IOException ioex) {
|
|
}
|
|
} catch (Exception ex1) {
|
|
// Do nothing.
|
|
}
|
|
return;
|
|
} catch (Exception ex) {
|
|
InternalErrorHandler.handleException(ex);
|
|
}
|
|
}
|
|
} finally {
|
|
this.isRunning = false;
|
|
this.tlsMessageProcessor.remove(this);
|
|
this.tlsMessageProcessor.useCount--;
|
|
this.myParser.close();
|
|
}
|
|
|
|
}
|
|
|
|
protected void uncache() {
|
|
if (isCached && !isRunning) {
|
|
this.tlsMessageProcessor.remove(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Equals predicate.
|
|
*
|
|
* @param other is the other object to compare ourselves to for equals
|
|
*/
|
|
|
|
public boolean equals(Object other) {
|
|
|
|
if (!this.getClass().equals(other.getClass()))
|
|
return false;
|
|
else {
|
|
TLSMessageChannel that = (TLSMessageChannel) other;
|
|
if (this.mySock != that.mySock)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an identifying key. This key is used to cache the connection and re-use it if
|
|
* necessary.
|
|
*/
|
|
public String getKey() {
|
|
if (this.key != null) {
|
|
return this.key;
|
|
} else {
|
|
this.key = MessageChannel.getKey(this.peerAddress, this.peerPort, "TLS");
|
|
return this.key;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the host to assign to outgoing messages.
|
|
*
|
|
* @return the host to assign to the via header.
|
|
*/
|
|
public String getViaHost() {
|
|
return myAddress;
|
|
}
|
|
|
|
/**
|
|
* Get the port for outgoing messages sent from the channel.
|
|
*
|
|
* @return the port to assign to the via header.
|
|
*/
|
|
public int getViaPort() {
|
|
return myPort;
|
|
}
|
|
|
|
/**
|
|
* Get the port of the peer to whom we are sending messages.
|
|
*
|
|
* @return the peer port.
|
|
*/
|
|
public int getPeerPort() {
|
|
return peerPort;
|
|
}
|
|
|
|
public int getPeerPacketSourcePort() {
|
|
return this.peerPort;
|
|
}
|
|
|
|
public InetAddress getPeerPacketSourceAddress() {
|
|
return this.peerAddress;
|
|
}
|
|
|
|
/**
|
|
* TLS Is a secure protocol.
|
|
*/
|
|
public boolean isSecure() {
|
|
return true;
|
|
}
|
|
|
|
public void setHandshakeCompletedListener(
|
|
HandshakeCompletedListener handshakeCompletedListenerImpl) {
|
|
this.handshakeCompletedListener = handshakeCompletedListenerImpl;
|
|
}
|
|
|
|
/**
|
|
* @return the handshakeCompletedListener
|
|
*/
|
|
public HandshakeCompletedListenerImpl getHandshakeCompletedListener() {
|
|
return (HandshakeCompletedListenerImpl) handshakeCompletedListener;
|
|
}
|
|
}
|