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.InternalErrorHandler;
  import gov2.nist.core.ServerLogger;
  import gov2.nist.core.StackLogger;
  import gov2.nist.core.ThreadAuditor;
  import gov2.nist.javax2.sip.SIPConstants;
  import gov2.nist.javax2.sip.header.CSeq;
  import gov2.nist.javax2.sip.header.CallID;
  import gov2.nist.javax2.sip.header.From;
  import gov2.nist.javax2.sip.header.RequestLine;
  import gov2.nist.javax2.sip.header.StatusLine;
  import gov2.nist.javax2.sip.header.To;
  import gov2.nist.javax2.sip.header.Via;
  import gov2.nist.javax2.sip.header.ViaList;
  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.ParseExceptionListener;
  import gov2.nist.javax2.sip.parser.StringMsgParser;
  
  import java.io.IOException;
  import java.net.DatagramPacket;
  import java.net.DatagramSocket;
  import java.net.InetAddress;
  import java.text.ParseException;
  import java.util.Hashtable;
  import java.util.TimerTask;
  
  import javax2.sip.address.Hop;
  
  /*
   * Kim Kirby (Keyvoice) suggested that duplicate checking should be added to the
   * stack (later removed). Lamine Brahimi suggested a single threaded behavior
   * flag be added to this. Niklas Uhrberg suggested that thread pooling support
   * be added to this for performance and resource management. Peter Parnes found
   * a bug with this code that was sending it into an infinite loop when a bad
   * incoming message was parsed. Bug fix by viswashanti.kadiyala@antepo.com.
   * Hagai Sela addded fixes for NAT traversal. Jeroen van Bemmel fixed up for
   * buggy clients (such as windows messenger) and added code to return
   * BAD_REQUEST. David Alique fixed an address recording bug. Jeroen van Bemmel
   * fixed a performance issue where the stack was doing DNS lookups (potentially
   * unnecessary). Ricardo Bora (Natural Convergence ) added code that prevents
   * the stack from exitting when an exception is encountered.
   *
   */
  
  /**
   * This is the UDP Message handler that gets created when a UDP message needs to
   * be processed. The message is processed by creating a String Message parser
   * and invoking it on the message read from the UDP socket. The parsed structure
   * is handed off via a SIP stack request for further processing. This stack
   * structure isolates the message handling logic from the mechanics of sending
   * and recieving messages (which could be either udp or tcp.
   *
   *
   * @author M. Ranganathan <br/>
   *
   *
   *
   * @version 1.2 $Revision: 1.66 $ $Date: 2010/01/14 05:15:49 $
   */
  public class UDPMessageChannel extends MessageChannel implements
          ParseExceptionListener, Runnable, RawMessageChannel {
      
  
      /**
       * SIP Stack structure for this channel.
       */
      protected SIPTransactionStack sipStack;
  
      /**
       * The parser we are using for messages received from this channel.
       */
      protected StringMsgParser myParser;
  
      /**
       * Where we got the stuff from
       */
      private InetAddress peerAddress;
  
      private String myAddress;
  
      private int peerPacketSourcePort;
  
      private InetAddress peerPacketSourceAddress;
  
      /**
       * Reciever port -- port of the destination.
       */
      private int peerPort;
  
      /**
       * Protocol to use when talking to receiver (i.e. when sending replies).
       */
      private String peerProtocol;
  
      protected int myPort;
  
      private DatagramPacket incomingPacket;
  
      private long receptionTime;
      
      /*
       * A table that keeps track of when the last pingback was sent to a given remote IP address
       * and port. This is for NAT compensation. This stays in the table for 1 seconds and prevents 
       * infinite loop. If a second pingback happens in that period of time, it will be dropped.
       */
      private Hashtable<String,PingBackTimerTask> pingBackRecord = new Hashtable<String,PingBackTimerTask>();
      
      class PingBackTimerTask extends TimerTask {
          String ipAddress;
          int port;
          
          public PingBackTimerTask(String ipAddress, int port) {
              this.ipAddress = ipAddress;
              this.port = port;
              pingBackRecord.put(ipAddress + ":" + port, this);
          }
          @Override
          public void run() {
             pingBackRecord.remove(ipAddress + ":" + port);
          }
          @Override
          public int hashCode() {
              return (ipAddress + ":" + port).hashCode();
          }
      }
  
      /**
       * Constructor - takes a datagram packet and a stack structure Extracts the
       * address of the other from the datagram packet and stashes away the
       * pointer to the passed stack structure.
       *
       * @param stack
       *            is the shared SIPStack structure
       * @param messageProcessor
       *            is the creating message processor.
       */
      protected UDPMessageChannel(SIPTransactionStack stack,
              UDPMessageProcessor messageProcessor) {
          super.messageProcessor = messageProcessor;
          this.sipStack = stack;
  
          Thread mythread = new Thread(this);
  
          this.myAddress = messageProcessor.getIpAddress().getHostAddress();
          this.myPort = messageProcessor.getPort();
  
          mythread.setName("UDPMessageChannelThread");
          mythread.setDaemon(true);
          mythread.start();
  
      }
  
      /**
       * Constructor. We create one of these in order to process an incoming
       * message.
       *
       * @param stack
       *            is the SIP sipStack.
       * @param messageProcessor
       *            is the creating message processor.
       * @param packet
       *            is the incoming datagram packet.
       */
      protected UDPMessageChannel(SIPTransactionStack stack,
              UDPMessageProcessor messageProcessor, DatagramPacket packet) {
  
          this.incomingPacket = packet;
          super.messageProcessor = messageProcessor;
          this.sipStack = stack;
  
          this.myAddress = messageProcessor.getIpAddress().getHostAddress();
          this.myPort = messageProcessor.getPort();
          Thread mythread = new Thread(this);
          mythread.setDaemon(true);
          mythread.setName("UDPMessageChannelThread");
  
          mythread.start();
  
      }
  
      /**
       * Constructor. We create one of these when we send out a message.
       *
       * @param targetAddr
       *            INET address of the place where we want to send messages.
       * @param port
       *            target port (where we want to send the message).
       * @param sipStack
       *            our SIP Stack.
       */
      protected UDPMessageChannel(InetAddress targetAddr, int port,
              SIPTransactionStack sipStack, UDPMessageProcessor messageProcessor) {
          peerAddress = targetAddr;
          peerPort = port;
          peerProtocol = "UDP";
          super.messageProcessor = messageProcessor;
          this.myAddress = messageProcessor.getIpAddress().getHostAddress();
          this.myPort = messageProcessor.getPort();
          this.sipStack = sipStack;
          if (sipStack.isLoggingEnabled()) {
              this.sipStack.getStackLogger().logDebug("Creating message channel "
                      + targetAddr.getHostAddress() + "/" + port);
          }
      }
  
      /**
       * Run method specified by runnnable.
       */
      public void run() {
          // Assume no thread pooling (bug fix by spierhj)
          ThreadAuditor.ThreadHandle threadHandle = null;
  
          while (true) {
              // Create a new string message parser to parse the list of messages.
              if (myParser == null) {
                  myParser = new StringMsgParser();
                  myParser.setParseExceptionListener(this);
              }
              // messages that we write out to him.
              DatagramPacket packet;
  
              if (sipStack.threadPoolSize != -1) {
                  synchronized (((UDPMessageProcessor) messageProcessor).messageQueue) {
                      while (((UDPMessageProcessor) messageProcessor).messageQueue
                              .isEmpty()) {
                          // Check to see if we need to exit.
                          if (!((UDPMessageProcessor) messageProcessor).isRunning)
                              return;
                          try {
                              // We're part of a thread pool. Ask the auditor to
                              // monitor this thread.
                              if (threadHandle == null) {
                                  threadHandle = sipStack.getThreadAuditor()
                                          .addCurrentThread();
                              }
  
                              // Send a heartbeat to the thread auditor
                              threadHandle.ping();
  
                              // Wait for packets
                              // Note: getPingInterval returns 0 (infinite) if the
                              // thread auditor is disabled.
                              ((UDPMessageProcessor) messageProcessor).messageQueue
                                      .wait(threadHandle
                                              .getPingIntervalInMillisecs());
                          } catch (InterruptedException ex) {
                              if (!((UDPMessageProcessor) messageProcessor).isRunning)
                                  return;
                          }
                      }
                      packet = (DatagramPacket) ((UDPMessageProcessor) messageProcessor).messageQueue
                              .removeFirst();
  
                  }
                  this.incomingPacket = packet;
              } else {
                  packet = this.incomingPacket;
              }
  
              // Process the packet. Catch and log any exception we may throw.
              try {
                  processIncomingDataPacket(packet);
              } catch (Exception e) {
  
                  if (sipStack.isLoggingEnabled())
                  	sipStack.getStackLogger().logError(
                          "Error while processing incoming UDP packet", e);
              }
  
              if (sipStack.threadPoolSize == -1) {
                  return;
              }
          }
      }
  
      /**
       * Process an incoming datagram
       *
       * @param packet
       *            is the incoming datagram packet.
       */
      private void processIncomingDataPacket(DatagramPacket packet)
              throws Exception {
          this.peerAddress = packet.getAddress();
          int packetLength = packet.getLength();
          // Read bytes and put it in a eueue.
          byte[] bytes = packet.getData();
          byte[] msgBytes = new byte[packetLength];
          System.arraycopy(bytes, 0, msgBytes, 0, packetLength);
  
          // Do debug logging.
          if (sipStack.isLoggingEnabled()) {
              this.sipStack.getStackLogger()
                      .logDebug("UDPMessageChannel: processIncomingDataPacket : peerAddress = "
                              + peerAddress.getHostAddress() + "/"
                              + packet.getPort() + " Length = " + packetLength);
  
          }
  
          SIPMessage sipMessage = null;
          try {
              this.receptionTime = System.currentTimeMillis();
              sipMessage = myParser.parseSIPMessage(msgBytes);
              myParser = null;
          } catch (ParseException ex) {
              myParser = null; // let go of the parser reference.
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug("Rejecting message !  "
                          + new String(msgBytes));
                  this.sipStack.getStackLogger().logDebug("error message "
                          + ex.getMessage());
                  this.sipStack.getStackLogger().logException(ex);
              }
  
  
              // JvB: send a 400 response for requests (except ACK)
              // Currently only UDP, @todo also other transports
              String msgString = new String(msgBytes, 0, packetLength);
              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, peerAddress,
                                  packet.getPort(), "UDP", 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");
                      }
                  }
              }
  
              return;
          }
          // No parse exception but null message - reject it and
          // march on (or return).
          // exit this message processor if the message did not parse.
  
          if (sipMessage == null) {
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug("Rejecting message !  + Null message parsed.");
              }
              if (pingBackRecord.get(packet.getAddress().getHostAddress() + ":" + packet.getPort()) == null ) {
                  byte[] retval = "\r\n\r\n".getBytes();
                  DatagramPacket keepalive = new DatagramPacket(retval,0,retval.length,packet.getAddress(),packet.getPort());
                  ((UDPMessageProcessor)this.messageProcessor).sock.send(keepalive);
                  this.sipStack.getTimer().schedule(new PingBackTimerTask(packet.getAddress().getHostAddress(), 
                              packet.getPort()), 1000);                
              }
              return;
          }
          ViaList viaList = sipMessage.getViaHeaders();
          // Check for the required headers.
          if (sipMessage.getFrom() == null || sipMessage.getTo() == null
                  || sipMessage.getCallId() == null
                  || sipMessage.getCSeq() == null
                  || sipMessage.getViaHeaders() == null) {
              String badmsg = new String(msgBytes);
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logError("bad message " + badmsg);
                  this.sipStack.getStackLogger().logError(">>> Dropped Bad Msg "
                          + "From = " + sipMessage.getFrom() + "To = "
                          + sipMessage.getTo() + "CallId = "
                          + sipMessage.getCallId() + "CSeq = "
                          + sipMessage.getCSeq() + "Via = "
                          + sipMessage.getViaHeaders());
              }
              return;
          }
          // For a request first via header tells where the message
          // is coming from.
          // For response, just get the port from the packet.
          if (sipMessage instanceof SIPRequest) {
              Via v = (Via) viaList.getFirst();
              Hop hop = sipStack.addressResolver.resolveAddress(v.getHop());
              this.peerPort = hop.getPort();
              this.peerProtocol = v.getTransport();
  
              this.peerPacketSourceAddress = packet.getAddress();
              this.peerPacketSourcePort = packet.getPort();
              try {
                  this.peerAddress = packet.getAddress();
                  // Check to see if the received parameter matches
                  // the peer address and tag it appropriately.
  
  
                  boolean hasRPort = v.hasParameter(Via.RPORT);
                  if (hasRPort
                          || !hop.getHost().equals(
                                  this.peerAddress.getHostAddress())) {
                      v.setParameter(Via.RECEIVED, this.peerAddress
                              .getHostAddress());
                  }
  
                  if (hasRPort) {
                      v.setParameter(Via.RPORT, Integer
                              .toString(this.peerPacketSourcePort));
                  }
              } catch (java.text.ParseException ex1) {
                  InternalErrorHandler.handleException(ex1);
              }
  
          } else {
  
              this.peerPacketSourceAddress = packet.getAddress();
              this.peerPacketSourcePort = packet.getPort();
              this.peerAddress = packet.getAddress();
              this.peerPort = packet.getPort();
              this.peerProtocol = ((Via) viaList.getFirst()).getTransport();
          }
  
          this.processMessage(sipMessage);
  
      }
  
      /**
       * Actually proces the parsed message.
       *
       * @param sipMessage
       */
      public void processMessage(SIPMessage sipMessage) {
  
          if (sipMessage instanceof SIPRequest) {
              SIPRequest sipRequest = (SIPRequest) sipMessage;
  
              // This is a request - process it.
              // So far so good -- we will commit this message if
              // all processing is OK.
              if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) {
  
                  this.sipStack.serverLogger.logMessage(sipMessage, this
                          .getPeerHostPort().toString(), this.getHost() + ":"
                          + this.myPort, false, receptionTime);
  
              }
              ServerRequestInterface sipServerRequest = sipStack
                      .newSIPServerRequest(sipRequest, this);
              // Drop it if there is no request returned
              if (sipServerRequest == null) {
                  if (sipStack.isLoggingEnabled()) {
                      this.sipStack.getStackLogger()
                              .logWarning("Null request interface returned -- dropping request");
                  }
  
  
                  return;
              }
              if (sipStack.isLoggingEnabled())
                  this.sipStack.getStackLogger().logDebug("About to process "
                          + sipRequest.getFirstLine() + "/" + sipServerRequest);
              try {
                  sipServerRequest.processRequest(sipRequest, this);
              } finally {
                  if (sipServerRequest instanceof SIPTransaction) {
                      SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest;
                      if (!sipServerTx.passToListener()) {
                          ((SIPTransaction) sipServerRequest).releaseSem();
                      }
                  }
              }
              if (sipStack.isLoggingEnabled())
                  this.sipStack.getStackLogger().logDebug("Done processing "
                          + sipRequest.getFirstLine() + "/" + sipServerRequest);
  
              // So far so good -- we will commit this message if
              // all processing is OK.
  
          } else {
              // Handle a SIP Reply message.
              SIPResponse sipResponse = (SIPResponse) sipMessage;
              try {
                  sipResponse.checkHeaders();
              } catch (ParseException ex) {
                  if (sipStack.isLoggingEnabled())
                      sipStack.getStackLogger()
                              .logError("Dropping Badly formatted response message >>> "
                                      + sipResponse);
                  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())
                          ((SIPTransaction) sipServerResponse).releaseSem();
                  }
  
                  // Normal processing of message.
              } else {
                  if (sipStack.isLoggingEnabled()) {
                      this.sipStack.getStackLogger().logDebug("null sipServerResponse!");
                  }
              }
  
          }
      }
  
      /**
       * JvB: added method to check for known buggy clients (Windows Messenger) to
       * fix the port to which responses are sent
       *
       * checks for User-Agent: RTC/1.3.5470 (Messenger 5.1.0701)
       *
       * JvB 22/7/2006 better to take this out for the moment, it is only a
       * problem in rare cases (unregister)
       *
       * private final boolean isBuggyClient( SIPRequest r ) { UserAgent uah =
       * (UserAgent) r.getHeader( UserAgent.NAME ); if (uah!=null) {
       * java.util.ListIterator i = uah.getProduct(); if (i.hasNext()) { String p =
       * (String) uah.getProduct().next(); return p.startsWith( "RTC" ); } }
       * return false; }
       */
  
      /**
       * Implementation of the ParseExceptionListener interface.
       *
       * @param ex
       *            Exception that is given to us by the parser.
       * @throws ParseException
       *             If we choose to reject the header or message.
       */
      public void handleException(ParseException ex, SIPMessage sipMessage,
              Class hdrClass, String header, String message)
              throws ParseException {
          if (sipStack.isLoggingEnabled())
              this.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().logError("BAD MESSAGE!");
              	sipStack.getStackLogger().logError(message);
          	}
              throw ex;
          } else {
              sipMessage.addUnparsed(header);
          }
      }
  
      /**
       * Return a reply from a pre-constructed reply. This sends the message back
       * to the entity who caused us to create this channel in the first place.
       *
       * @param sipMessage
       *            Message string to send.
       * @throws IOException
       *             If there is a problem with sending the message.
       */
      public void sendMessage(SIPMessage sipMessage) throws IOException {
          if (sipStack.isLoggingEnabled() && this.sipStack.isLogStackTraceOnMessageSend()) {
              if ( sipMessage instanceof SIPRequest &&
                      ((SIPRequest)sipMessage).getRequestLine() != null) {
                  /*
                   * We dont want to log empty trace messages.
                   */
                  this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO);
              } else {
                  this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO);
              }
          }
  
          // Test and see where we are going to send the messsage. If the message
          // is sent back to oursleves, just
          // shortcircuit processing.
          long time = System.currentTimeMillis();
          try {
              for (MessageProcessor messageProcessor : sipStack
                      .getMessageProcessors()) {
                  if (messageProcessor.getIpAddress().equals(this.peerAddress)
                          && messageProcessor.getPort() == this.peerPort
                          && messageProcessor.getTransport().equals(
                                  this.peerProtocol)) {
                      MessageChannel messageChannel = messageProcessor
                              .createMessageChannel(this.peerAddress,
                                      this.peerPort);
                      if (messageChannel instanceof RawMessageChannel) {
                          ((RawMessageChannel) messageChannel)
                                  .processMessage(sipMessage);
                          if (sipStack.isLoggingEnabled())
                          	sipStack.getStackLogger().logDebug("Self routing message");
                          return;
                      }
  
                  }
              }
  
              sendMessage(sipMessage, peerAddress, peerPort, peerProtocol,
                      sipMessage instanceof SIPRequest);
  
          } catch (IOException ex) {
              throw ex;
          } catch (Exception ex) {
              if (sipStack.isLoggingEnabled())
              	sipStack.getStackLogger().logError("An exception occured while sending message",ex);
              throw new IOException(
                      "An exception occured while sending message");
          } finally {
              if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES) && !sipMessage.isNullRequest())
                  logMessage(sipMessage, peerAddress, peerPort, time);
              else if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_DEBUG))
                  sipStack.getStackLogger().logDebug("Sent EMPTY Message");
          }
      }
  
      /**
       * Send a message to a specified receiver address.
       *
       * @param message
       *            string to send.
       * @param peerAddress
       *            Address of the place to send it to.
       * @param peerPort
       *            the port to send it to.
       * @throws IOException
       *             If there is trouble sending this message.
       */
      protected void sendMessage(SIPMessage message, InetAddress peerAddress,
              int peerPort, boolean reConnect) throws IOException {
          // Via is not included in the request so silently drop the reply.
          if (sipStack.isLoggingEnabled() && this.sipStack.isLogStackTraceOnMessageSend() ) {
              this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO);
          }
          if (peerPort == -1) {
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug(getClass().getName()
                          + ":sendMessage: Dropping reply!");
              }
              throw new IOException("Receiver port not set ");
          } else {
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug("sendMessage " + peerAddress.getHostAddress() + "/"
                          + peerPort + "\n" ) ;
                  this.sipStack.getStackLogger().logDebug("*******************\n");
              }
  
          }
          byte[] msg = message.encodeAsBytes("udp");
          DatagramPacket reply = new DatagramPacket(msg, msg.length, peerAddress,
                  peerPort);
          try {
              DatagramSocket sock;
              boolean created = false;
  
              if (sipStack.udpFlag) {
                  // Use the socket from the message processor (for firewall
                  // support use the same socket as the message processor
                  // socket -- feature request # 18 from java.net). This also
                  // makes the whole thing run faster!
                  sock = ((UDPMessageProcessor) messageProcessor).sock;
  
                  // Bind the socket to the stack address in case there
                  // are multiple interfaces on the machine (feature reqeust
                  // by Will Scullin) 0 binds to an ephemeral port.
                  // sock = new DatagramSocket(0,sipStack.stackInetAddress);
              } else {
                  // bind to any interface and port.
                  sock = new DatagramSocket();
                  created = true;
              }
              sock.send(reply);
              if (created)
                  sock.close();
          } catch (IOException ex) {
              throw ex;
          } catch (Exception ex) {
              InternalErrorHandler.handleException(ex);
          }
      }
  
      /**
       * Send a message to a specified receiver address.
       *
       * @param message
       *            message string to send.
       * @param peerAddress
       *            Address of the place to send it to.
       * @param peerPort
       *            the port to send it to.
       * @param peerProtocol
       *            protocol to use to send.
       * @throws IOException
       *             If there is trouble sending this message.
       */
      protected void sendMessage(SIPMessage message, InetAddress peerAddress,
              int peerPort, String peerProtocol, boolean retry)
              throws IOException {
          // Via is not included in the request so silently drop the reply.
          if (peerPort == -1) {
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug(getClass().getName()
                          + ":sendMessage: Dropping reply!");
              }
              throw new IOException("Receiver port not set ");
          } else {
              if (sipStack.isLoggingEnabled()) {
                  this.sipStack.getStackLogger().logDebug( ":sendMessage " + peerAddress.getHostAddress() + "/"
                          + peerPort + "\n");
              }
          }
          if (peerProtocol.compareToIgnoreCase("UDP") == 0) {
          	byte[] msg = message.encodeAsBytes("udp");
              DatagramPacket reply = new DatagramPacket(msg, msg.length,
                      peerAddress, peerPort);
  
              try {
                  DatagramSocket sock;
                  if (sipStack.udpFlag) {
                      sock = ((UDPMessageProcessor) messageProcessor).sock;
  
                  } else {
                      // bind to any interface and port.
                      sock = sipStack.getNetworkLayer().createDatagramSocket();
                  }
                  if (sipStack.isLoggingEnabled()) {
                      this.sipStack.getStackLogger().logDebug("sendMessage "
                              + peerAddress.getHostAddress() + "/" + peerPort
                              + "\n" + new String(msg));
                  }
                  sock.send(reply);
                  if (!sipStack.udpFlag)
                      sock.close();
              } catch (IOException ex) {
                  throw ex;
              } catch (Exception ex) {
                  InternalErrorHandler.handleException(ex);
              }
  
          } else {
              // Use TCP to talk back to the sender.
              sipStack.ioHandler.sendBytes(
                      this.messageProcessor.getIpAddress(), peerAddress,
                      peerPort, "tcp", message, retry,this);
          }
      }
  
      /**
       * get the stack pointer.
       *
       * @return The sip stack for this channel.
       */
      public SIPTransactionStack getSIPStack() {
          return sipStack;
      }
  
      /**
       * Return a transport string.
       *
       * @return the string "udp" in this case.
       */
      public String getTransport() {
          return SIPConstants.UDP;
      }
  
      /**
       * get the stack address for the stack that received this message.
       *
       * @return The stack address for our sipStack.
       */
      public String getHost() {
          return messageProcessor.getIpAddress().getHostAddress();
      }
  
      /**
       * get the port.
       *
       * @return Our port (on which we are getting datagram packets).
       */
      public int getPort() {
          return ((UDPMessageProcessor) messageProcessor).getPort();
      }
  
      /**
       * get the name (address) of the host that sent me the message
       *
       * @return The name of the sender (from the datagram packet).
       */
      public String getPeerName() {
          return peerAddress.getHostName();
      }
  
      /**
       * get the address of the host that sent me the message
       *
       * @return The senders ip address.
       */
      public String getPeerAddress() {
          return peerAddress.getHostAddress();
      }
  
      protected InetAddress getPeerInetAddress() {
          return peerAddress;
      }
  
      /**
       * Compare two UDP Message channels for equality.
       *
       * @param other
       *            The other message channel with which to compare oursleves.
       */
      public boolean equals(Object other) {
  
          if (other == null)
              return false;
          boolean retval;
          if (!this.getClass().equals(other.getClass())) {
              retval = false;
          } else {
              UDPMessageChannel that = (UDPMessageChannel) other;
              retval = this.getKey().equals(that.getKey());
          }
  
          return retval;
      }
  
      public String getKey() {
          return getKey(peerAddress, peerPort, "UDP");
      }
  
      public int getPeerPacketSourcePort() {
          return peerPacketSourcePort;
      }
  
      public InetAddress getPeerPacketSourceAddress() {
          return peerPacketSourceAddress;
      }
  
      /**
       * Get the logical originator of the message (from the top via header).
       *
       * @return topmost via header sentby field
       */
      public String getViaHost() {
          return this.myAddress;
      }
  
      /**
       * Get the logical port of the message orginator (from the top via hdr).
       *
       * @return the via port from the topmost via header.
       */
      public int getViaPort() {
          return this.myPort;
      }
  
      /**
       * Returns "false" as this is an unreliable transport.
       */
      public boolean isReliable() {
          return false;
      }
  
      /**
       * UDP is not a secure protocol.
       */
      public boolean isSecure() {
          return false;
      }
  
      public int getPeerPort() {
          return peerPort;
      }
  
      public String getPeerProtocol() {
          return this.peerProtocol;
      }
  
      /**
       * Close the message channel.
       */
      public void close() {
      }
  
  
  }
  