/**
   * Send a message to a specified receiver address.
   *
   * @param msg message string to send.
   * @param receiverAddress Address of the place to send it to.
   * @param receiverPort the port to send it to.
   * @param receiverProtocol protocol to use to send.
   * @param retry try if connection was not successful at fi
   * @throws IOException If there is trouble sending this message.
   */
  protected void sendMessage(
      byte[] msg, String receiverAddress, int receiverPort, String receiverProtocol, boolean retry)
      throws IOException {
    if (LogWriter.needsLogging)
      LogWriter.logMessage(
          "Sending message to ["
              + receiverAddress
              + ":"
              + receiverPort
              + "/"
              + receiverProtocol
              + "]\n"
              + new String(msg)
              + "  to be sent to  ");

    // msg += "\r\n\r\n";
    // Via is not included in the request so silently drop the reply.
    if (receiverPort == -1) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            "DEBUG, UDPMessageChannel, sendMessage(),"
                + " The message is not sent: the receiverPort=-1");
      throw new IOException("Receiver port not set ");
    }
    if (Utils.compareToIgnoreCase(receiverProtocol, "UDP") == 0) {

      try {
        DatagramConnection socket;
        Datagram reply;
        boolean created = false;

        if (stack.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!
          socket = ((UDPMessageProcessor) messageProcessor).dc;

          // ArnauVP: this has the problem that the datagram created from this (inbound) connection
          // doesn't have an address assigned. Let's do it.
          reply = socket.newDatagram(msg, msg.length);
          reply.setAddress("datagram://" + peerAddress + ":" + peerPort);

        } else {
          // bind to any interface and port.
          // format: datagram://address:port
          socket = stack.getNetworkLayer().createDatagramSocket(peerAddress, peerPort);
          reply = socket.newDatagram(msg, msg.length);
          created = true;
        }
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              LogWriter.TRACE_DEBUG,
              "UDPMessageChannel, sendMessage(),"
                  + " Sending message over UDP, using socket "
                  + socket
                  + " created "
                  + created
                  + " destination "
                  + reply.getAddress());
        socket.send(reply);
        if (created) socket.close();
      } catch (IOException ex) {
        throw ex;
      } catch (Exception ex) {
        InternalErrorHandler.handleException(ex);
      }

    } else {
      // Use TCP to talk back to the sender.
      SocketConnection outputSocket =
          stack.ioHandler.sendBytes(peerAddress, peerPort, "tcp", msg, retry);
      OutputStream myOutputStream = stack.ioHandler.getSocketOutputStream(outputSocket);
      myOutputStream.write(msg, 0, msg.length);
      myOutputStream.flush();
      // The socket is cached (don't close it!);
    }
  }
  /**
   * Process an incoming datagram
   *
   * @param packet is the incoming datagram packet.
   */
  private void processIncomingDataPacket(Datagram packet) throws Exception {

    // For a request first via header tells where the message
    // is coming from.
    // For response, just get the port from the packet.
    // format: address:port
    String address = packet.getAddress();
    try {
      int firstColon = address.indexOf("//");
      int secondColon = address.indexOf(":", firstColon + 1);
      this.peerAddress = address.substring(firstColon + 2, secondColon);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender address:" + peerAddress);
      String senderPortString = address.substring(address.indexOf(";") + 1, address.indexOf("|"));
      this.peerPacketSourcePort = Integer.parseInt(senderPortString);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender port:" + peerPacketSourcePort);
    } catch (NumberFormatException e) {

      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_EXCEPTION,
            "UDPMessageChannel, run(), exception raised: " + e.getMessage());
      e.printStackTrace();
      peerPacketSourcePort = -1;
    }

    int packetLength = packet.getLength();
    // Read bytes and put it in a queue.
    byte[] msgBytes = packet.getData();

    // Do debug logging.
    if (LogWriter.needsLogging) {
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "UDPMessageChannel: processIncomingDataPacket : peerAddress = "
              + peerAddress
              + "/"
              + peerPacketSourcePort
              + " Length = "
              + packetLength
              + " msgBytes "
              + msgBytes);
    }

    Message sipMessage = null;
    try {
      receptionTime = System.currentTimeMillis();
      sipMessage = myParser.parseSIPMessage(msgBytes);
      myParser = null;
    } catch (ParseException ex) {
      myParser = null; // let go of the parser reference.
      if (LogWriter.needsLogging) {
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  " + new String(msgBytes));
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "error message " + ex.getMessage());
        LogWriter.logException(ex);
      }

      // TODO: do this on TCP too
      // JvB: send a 400 response for requests (except ACK)
      String msgString = new String(msgBytes, 0, packetLength);
      if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {

        String badReqRes = create400Response(msgString, ex);
        if (badReqRes != null) {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "Sending automatic 400 Bad Request: " + badReqRes);
          try {
            this.sendMessage(badReqRes.getBytes(), peerAddress, peerPacketSourcePort, "UDP", false);
          } catch (IOException e) {
            LogWriter.logException(e);
          }
        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "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 (LogWriter.needsLogging)
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  + Null message parsed.");
      return;
    }

    ViaList viaList = sipMessage.getViaHeaders();

    // Check for the required headers.
    if (sipMessage.getFromHeader() == null
        ||
        // sipMessage.getFromHeader().getTag() == null  ||
        sipMessage.getTo() == null
        || sipMessage.getCallId() == null
        || sipMessage.getCSeqHeader() == null
        || sipMessage.getViaHeaders() == null) {
      String badmsg = new String(msgBytes);
      if (LogWriter.needsLogging) {
        LogWriter.logMessage("bad message " + badmsg);
        LogWriter.logMessage(
            ">>> Dropped Bad Msg "
                + "FromHeader = "
                + sipMessage.getFromHeader()
                + "ToHeader = "
                + sipMessage.getTo()
                + "CallId = "
                + sipMessage.getCallId()
                + "CSeqHeader = "
                + sipMessage.getCSeqHeader()
                + "Via = "
                + sipMessage.getViaHeaders());
      }

      stack.logBadMessage(badmsg);
      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 Request) {

      ViaHeader v = (ViaHeader) viaList.first();
      if (v.hasPort()) this.peerPort = v.getPort();
      else this.peerPort = SIPMessageStack.DEFAULT_PORT;

      this.peerProtocol = v.getTransport();

      boolean hasRPort = v.hasParameter(ViaHeader.RPORT);
      // Be warned, the host comparison may fail if socket.getAddress()
      // returns a domain name as the Via Host will be a numeric IP.
      // FIXME: No idea. Doing a DNS lookup or reverse DNS lookup
      // can be misleading because they can be non-matching, that is,
      // DNS(peerAddressName) != ReverseDNS(peerAddressIP)
      if (hasRPort || !this.peerAddress.equals(v.getHost())) {
        if (LogWriter.needsLogging)
          LogWriter.logMessage(
              LogWriter.TRACE_MESSAGES,
              "WARNING! \"Received\" parameter "
                  + "has been temporarily disabled. Response will be sent to topmost Via Host: "
                  + v.getHost());
        this.peerAddress = v.getHost();
        //                if (LogWriter.needsLogging)
        //	                   LogWriter.logMessage(LogWriter.TRACE_MESSAGES, "Adding \"received\"
        // parameter" +
        //	                   		" to incoming request with value: " + peerAddress +
        //	                   		" because it doesn't match the Via host " + v.getHost());
        //				v.setParameter(ViaHeader.RECEIVED, this.peerAddress);
      }

      if (hasRPort) {
        v.setParameter(ViaHeader.RPORT, Integer.toString(peerPacketSourcePort));
        this.peerPort = peerPacketSourcePort;
      }

    } else {
      this.peerPort = this.peerPacketSourcePort;
      this.peerProtocol = ((ViaHeader) viaList.getFirst()).getTransport();
    }

    processMessage(sipMessage);
  }