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
   *  
   * .
   * 
   */
  /******************************************************************************
   * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).       *
   ******************************************************************************/
  
  package gov2.nist.javax2.sip.stack;
  
  import gov2.nist.core.Host;
  import gov2.nist.core.HostPort;
  import gov2.nist.core.InternalErrorHandler;
  import gov2.nist.core.ServerLogger;
  import gov2.nist.javax2.sip.address.AddressImpl;
  import gov2.nist.javax2.sip.header.ContentLength;
  import gov2.nist.javax2.sip.header.ContentType;
  import gov2.nist.javax2.sip.header.Via;
  import gov2.nist.javax2.sip.message.MessageFactoryImpl;
  import gov2.nist.javax2.sip.message.SIPMessage;
  import gov2.nist.javax2.sip.message.SIPRequest;
  import gov2.nist.javax2.sip.message.SIPResponse;
  import gov2.nist.javax2.sip.parser.StringMsgParser;
  
  import java.io.IOException;
  import java.net.InetAddress;
  import java.text.ParseException;
  
  import javax2.sip.address.Hop;
  import javax2.sip.header.CSeqHeader;
  import javax2.sip.header.CallIdHeader;
  import javax2.sip.header.ContactHeader;
  import javax2.sip.header.ContentLengthHeader;
  import javax2.sip.header.ContentTypeHeader;
  import javax2.sip.header.FromHeader;
  import javax2.sip.header.ServerHeader;
  import javax2.sip.header.ToHeader;
  import javax2.sip.header.ViaHeader;
  
  /**
   * Message channel abstraction for the SIP stack.
   * 
   * @author M. Ranganathan <br/> Contains additions for support of symmetric NAT contributed by
   *         Hagai.
   * 
   * @version 1.2 $Revision: 1.28 $ $Date: 2009/11/14 20:06:18 $
   * 
   * 
   */
  public abstract class MessageChannel {
  
      // Incremented whenever a transaction gets assigned
      // to the message channel and decremented when
      // a transaction gets freed from the message channel.
  	protected int useCount;
  	
  	/**
  	 * Hook method, overridden by subclasses
  	 */
  	protected void uncache() {}
  	
      /**
       * Message processor to whom I belong (if set).
       */
      protected transient MessageProcessor messageProcessor;
  
      /**
       * Close the message channel.
       */
      public abstract void close();
  
      /**
       * Get the SIPStack object from this message channel.
       * 
       * @return SIPStack object of this message channel
       */
      public abstract SIPTransactionStack getSIPStack();
  
      /**
       * Get transport string of this message channel.
       * 
       * @return Transport string of this message channel.
       */
      public abstract String getTransport();
  
      /**
       * Get whether this channel is reliable or not.
       * 
       * @return True if reliable, false if not.
       */
      public abstract boolean isReliable();
  
      /**
       * Return true if this is a secure channel.
       */
      public abstract boolean isSecure();
  
      /**
       * Send the message (after it has been formatted)
       * 
       * @param sipMessage Message to send.
       */
      public abstract void sendMessage(SIPMessage sipMessage) throws IOException;
  
      /**
       * Get the peer address of the machine that sent us this message.
       * 
       * @return a string contianing the ip address or host name of the sender of the message.
       */
      public abstract String getPeerAddress();
  
      protected abstract InetAddress getPeerInetAddress();
  
      protected abstract String getPeerProtocol();
  
      /**
       * Get the sender port ( the port of the other end that sent me the message).
       */
      public abstract int getPeerPort();
  
      public abstract int getPeerPacketSourcePort();
  
      public abstract InetAddress getPeerPacketSourceAddress();
  
      /**
       * Generate a key which identifies the message channel. This allows us to cache the message
       * channel.
       */
      public abstract String getKey();
  
      /**
       * Get the host to assign for an outgoing Request via header.
       */
      public abstract String getViaHost();
  
      /**
       * Get the port to assign for the via header of an outgoing message.
       */
      public abstract int getViaPort();
  
      /**
       * Send the message (after it has been formatted), to a specified address and a specified port
       * 
       * @param message Message to send.
       * @param receiverAddress Address of the receiver.
       * @param receiverPort Port of the receiver.
       */
      protected abstract void sendMessage(SIPMessage message, InetAddress receiverAddress,
              int receiverPort, boolean reconnectFlag) throws IOException;
  
      /**
       * Get the host of this message channel.
       * 
       * @return host of this messsage channel.
       */
      public String getHost() {
          return this.getMessageProcessor().getIpAddress().getHostAddress();
      }
  
      /**
       * Get port of this message channel.
       * 
       * @return Port of this message channel.
       */
      public int getPort() {
          if (this.messageProcessor != null)
              return messageProcessor.getPort();
          else
              return -1;
      }
  
      /**
       * Send a formatted message to the specified target.
       * 
       * @param sipMessage Message to send.
       * @param hop hop to send it to.
       * @throws IOException If there is an error sending the message
       */
      public void sendMessage(SIPMessage sipMessage, Hop hop) throws IOException {
          long time = System.currentTimeMillis();
          InetAddress hopAddr = InetAddress.getByName(hop.getHost());
  
          try {
  
              for (MessageProcessor messageProcessor : getSIPStack().getMessageProcessors()) {
                  if (messageProcessor.getIpAddress().equals(hopAddr)
                          && messageProcessor.getPort() == hop.getPort()
                          && messageProcessor.getTransport().equals(hop.getTransport())) {
                      MessageChannel messageChannel = messageProcessor.createMessageChannel(
                              hopAddr, hop.getPort());
                      if (messageChannel instanceof RawMessageChannel) {
                          ((RawMessageChannel) messageChannel).processMessage(sipMessage);
                          if (getSIPStack().isLoggingEnabled())
                          	getSIPStack().getStackLogger().logDebug("Self routing message");
                          return;
                      }
  
                  }
              }
  
              this.sendMessage(sipMessage, hopAddr, hop.getPort(), sipMessage instanceof SIPRequest);
  
          } catch (IOException ioe) {
              throw ioe;
          } catch (Exception ex) {
          	if (this.getSIPStack().getStackLogger().isLoggingEnabled(ServerLogger.TRACE_ERROR)) {
          		this.getSIPStack().getStackLogger().logError("Error self routing message cause by: ", ex);
          	}
          	// TODO: When moving to Java 6, use the IOExcpetion(message, exception) constructor
              throw new IOException("Error self routing message");
          } finally {
  
              if (this.getSIPStack().getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES))
                  logMessage(sipMessage, hopAddr, hop.getPort(), time);
          }
      }
  
      /**
       * Send a message given SIP message.
       * 
       * @param sipMessage is the messge to send.
       * @param receiverAddress is the address to which we want to send
       * @param receiverPort is the port to which we want to send
       */
      public void sendMessage(SIPMessage sipMessage, InetAddress receiverAddress, int receiverPort)
              throws IOException {
          long time = System.currentTimeMillis();
          sendMessage(sipMessage, receiverAddress, receiverPort, sipMessage instanceof SIPRequest);
          logMessage(sipMessage, receiverAddress, receiverPort, time);
      }
  
      /**
       * Convenience function to get the raw IP source address of a SIP message as a String.
       */
      public String getRawIpSourceAddress() {
          String sourceAddress = getPeerAddress();
          String rawIpSourceAddress = null;
          try {
              InetAddress sourceInetAddress = InetAddress.getByName(sourceAddress);
              rawIpSourceAddress = sourceInetAddress.getHostAddress();
          } catch (Exception ex) {
              InternalErrorHandler.handleException(ex);
          }
          return rawIpSourceAddress;
      }
  
      /**
       * generate a key given the inet address port and transport.
       */
      public static String getKey(InetAddress inetAddr, int port, String transport) {
          return (transport + ":" + inetAddr.getHostAddress() + ":" + port).toLowerCase();
      }
  
      /**
       * Generate a key given host and port.
       */
      public static String getKey(HostPort hostPort, String transport) {
          return (transport + ":" + hostPort.getHost().getHostname() + ":" + hostPort.getPort())
                  .toLowerCase();
      }
  
      /**
       * Get the hostport structure of this message channel.
       */
      public HostPort getHostPort() {
          HostPort retval = new HostPort();
          retval.setHost(new Host(this.getHost()));
          retval.setPort(this.getPort());
          return retval;
      }
  
      /**
       * Get the peer host and port.
       * 
       * @return a HostPort structure for the peer.
       */
      public HostPort getPeerHostPort() {
          HostPort retval = new HostPort();
          retval.setHost(new Host(this.getPeerAddress()));
          retval.setPort(this.getPeerPort());
          return retval;
      }
  
      /**
       * Get the Via header for this transport. Note that this does not set a branch identifier.
       * 
       * @return a via header for outgoing messages sent from this channel.
       */
      public Via getViaHeader() {
          Via channelViaHeader;
  
          channelViaHeader = new Via();
          try {
              channelViaHeader.setTransport(getTransport());
          } catch (ParseException ex) {
          }
          channelViaHeader.setSentBy(getHostPort());
          return channelViaHeader;
      }
  
      /**
       * Get the via header host:port structure. This is extracted from the topmost via header of
       * the request.
       * 
       * @return a host:port structure
       */
      public HostPort getViaHostPort() {
          HostPort retval = new HostPort();
          retval.setHost(new Host(this.getViaHost()));
          retval.setPort(this.getViaPort());
          return retval;
      }
  
      /**
       * Log a message sent to an address and port via the default interface.
       * 
       * @param sipMessage is the message to log.
       * @param address is the inet address to which the message is sent.
       * @param port is the port to which the message is directed.
       */
      protected void logMessage(SIPMessage sipMessage, InetAddress address, int port, long time) {
          if (!getSIPStack().getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES))
              return;
  
          // Default port.
          if (port == -1)
              port = 5060;
          getSIPStack().serverLogger.logMessage(sipMessage, this.getHost() + ":" + this.getPort(),
                  address.getHostAddress().toString() + ":" + port, true, time);
      }
  
      /**
       * Log a response received at this message channel. This is used for processing incoming
       * responses to a client transaction.
       * 
       * @param receptionTime is the time at which the response was received.
       * @param status is the processing status of the message.
       * 
       */
      public void logResponse(SIPResponse sipResponse, long receptionTime, String status) {
          int peerport = getPeerPort();
          if (peerport == 0 && sipResponse.getContactHeaders() != null) {
              ContactHeader contact = (ContactHeader) sipResponse.getContactHeaders().getFirst();
              peerport = ((AddressImpl) contact.getAddress()).getPort();
  
          }
          String from = getPeerAddress().toString() + ":" + peerport;
          String to = this.getHost() + ":" + getPort();
          this.getSIPStack().serverLogger.logMessage(sipResponse, from, to, status, false,
                  receptionTime);
      }
  
      /**
       * Creates a response to a bad request (ie one that causes a ParseException)
       * 
       * @param badReq
       * @return message bytes, null if unable to formulate response
       */
      protected final SIPMessage createBadReqRes(String badReq, ParseException pe) {
  
          StringBuffer buf = new StringBuffer(512);
          buf.append("SIP/2.0 400 Bad Request (" + pe.getLocalizedMessage() + ')');
  
          // We need the following headers: all Vias, CSeq, Call-ID, From, To
          if (!copyViaHeaders(badReq, buf))
              return null;
          if (!copyHeader(CSeqHeader.NAME, badReq, buf))
              return null;
          if (!copyHeader(CallIdHeader.NAME, badReq, buf))
              return null;
          if (!copyHeader(FromHeader.NAME, badReq, buf))
              return null;
          if (!copyHeader(ToHeader.NAME, badReq, buf))
              return null;
  
          // Should add a to-tag if not already present...
          int toStart = buf.indexOf(ToHeader.NAME);
          if (toStart != -1 && buf.indexOf("tag", toStart) == -1) {
              buf.append(";tag=badreq");
          }
  
          // Let's add a Server header too..
          ServerHeader s = MessageFactoryImpl.getDefaultServerHeader();
          if ( s != null ) {
              buf.append("\r\n" + s.toString());
          }
          int clength = badReq.length();
          if (! (this instanceof UDPMessageChannel) ||
                  clength + buf.length() + ContentTypeHeader.NAME.length()
                  + ": message/sipfrag\r\n".length() +
                  ContentLengthHeader.NAME.length()  < 1300) { 
              
              /*
               * Check to see we are within one UDP packet.
               */
              ContentTypeHeader cth = new ContentType("message", "sipfrag");
              buf.append("\r\n" + cth.toString());
              ContentLength clengthHeader = new ContentLength(clength);
              buf.append("\r\n" + clengthHeader.toString());
              buf.append("\r\n\r\n" + badReq);
          } else {
              ContentLength clengthHeader = new ContentLength(0);
              buf.append("\r\n" + clengthHeader.toString());
          }
          
  		StringMsgParser msgParser = new StringMsgParser();
  		try {
  			return msgParser.parseSIPMessage(buf.toString().getBytes());
  		} catch (ParseException e) {
  			InternalErrorHandler.handleException(e);
  		}
          
          return null;
      }
  
      /**
       * Copies a header from a request
       * 
       * @param name
       * @param fromReq
       * @param buf
       * @return
       * 
       * Note: some limitations here: does not work for short forms of headers, or continuations;
       * problems when header names appear in other parts of the request
       */
      private static final boolean copyHeader(String name, String fromReq, StringBuffer buf) {
          int start = fromReq.indexOf(name);
          if (start != -1) {
              int end = fromReq.indexOf("\r\n", start);
              if (end != -1) {
                  // XX Assumes no continuation here...
                  buf.append(fromReq.subSequence(start - 2, end)); // incl CRLF
                  // in front
                  return true;
              }
          }
          return false;
      }
  
      /**
       * Copies all via headers from a request
       * 
       * @param fromReq
       * @param buf
       * @return
       * 
       * Note: some limitations here: does not work for short forms of headers, or continuations
       */
      private static final boolean copyViaHeaders(String fromReq, StringBuffer buf) {
          int start = fromReq.indexOf(ViaHeader.NAME);
          boolean found = false;
          while (start != -1) {
              int end = fromReq.indexOf("\r\n", start);
              if (end != -1) {
                  // XX Assumes no continuation here...
                  buf.append(fromReq.subSequence(start - 2, end)); // incl CRLF
                  // in front
                  found = true;
                  start = fromReq.indexOf(ViaHeader.NAME, end);
              } else {
                  return false;
              }
          }
          return found;
      }
  
      /**
       * Get the message processor.
       */
      public MessageProcessor getMessageProcessor() {
          return this.messageProcessor;
      }
  }
  