| /* |
| * 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; |
| } |
| } |