content: /*
   * 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 gov2.nist.javax2.sip.stack;
  
  import gov2.nist.core.*;
  import gov2.nist.javax2.sip.header.*;
  import gov2.nist.javax2.sip.message.*;
  import gov2.nist.javax2.sip.parser.*;
  
  import java.net.*;
  import java.io.*;
  import java.text.ParseException;
  
  import javax.net.ssl.HandshakeCompletedListener;
  import javax.net.ssl.SSLSocket;
  import javax2.sip.address.Hop;
  import javax2.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 gov2.nist.javax2.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 message is the message to send.
       * @param retry
       */
      private void sendMessage(SIPMessage message, boolean retry) throws IOException {
          Socket sock = this.sipStack.ioHandler.sendBytes(
                  this.getMessageProcessor().getIpAddress(), this.peerAddress, this.peerPort,
                  this.peerProtocol, message, 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 {
  
          long time = System.currentTimeMillis();
  
          this.sendMessage(sipMessage, 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 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(SIPMessage 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 ")) {
  
                  SIPMessage badReqRes = createBadReqRes(msgString, ex);
                  if (badReqRes != null) {
                      if (sipStack.isLoggingEnabled()) {
                          sipStack.getStackLogger().logDebug("Sending automatic 400 Bad Request:");
                          sipStack.getStackLogger().logDebug(msgString);
                      }
                      try {
                          this.sendMessage(badReqRes, this.getPeerInetAddress(), this
                                  .getPeerPort(), false);
                      } catch (IOException e) {
                          if (sipStack.isLoggingEnabled())
                          	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);
                      this.sendMessage(sipResponse, 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 {
                      if (sipStack.isLoggingEnabled())
                      	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;
      }
  }
  