| /* |
| * 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 gov.nist.javax.sip.stack; |
| |
| import gov.nist.core.InternalErrorHandler; |
| import gov.nist.core.ServerLogger; |
| import gov.nist.core.StackLogger; |
| import gov.nist.core.ThreadAuditor; |
| import gov.nist.javax.sip.SIPConstants; |
| import gov.nist.javax.sip.header.CSeq; |
| import gov.nist.javax.sip.header.CallID; |
| import gov.nist.javax.sip.header.From; |
| import gov.nist.javax.sip.header.RequestLine; |
| import gov.nist.javax.sip.header.StatusLine; |
| import gov.nist.javax.sip.header.To; |
| import gov.nist.javax.sip.header.Via; |
| import gov.nist.javax.sip.header.ViaList; |
| import gov.nist.javax.sip.message.SIPMessage; |
| import gov.nist.javax.sip.message.SIPRequest; |
| import gov.nist.javax.sip.message.SIPResponse; |
| import gov.nist.javax.sip.parser.ParseExceptionListener; |
| import gov.nist.javax.sip.parser.StringMsgParser; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.DatagramPacket; |
| import java.net.DatagramSocket; |
| import java.net.InetAddress; |
| import java.net.Socket; |
| import java.text.ParseException; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.TimerTask; |
| |
| import javax.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) { |
| |
| 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 ")) { |
| |
| String badReqRes = createBadReqRes(msgString, ex); |
| if (badReqRes != null) { |
| if (sipStack.isLoggingEnabled()) { |
| sipStack.getStackLogger().logDebug( |
| "Sending automatic 400 Bad Request:"); |
| sipStack.getStackLogger().logDebug(badReqRes); |
| } |
| try { |
| this.sendMessage(badReqRes.getBytes(), peerAddress, |
| packet.getPort(), "UDP", false); |
| } catch (IOException e) { |
| 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; |
| } |
| |
| } |
| } |
| |
| byte[] msg = sipMessage.encodeAsBytes( this.getTransport() ); |
| |
| sendMessage(msg, peerAddress, peerPort, peerProtocol, |
| sipMessage instanceof SIPRequest); |
| |
| } catch (IOException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| 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 msg |
| * 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(byte[] msg, 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" + "messageSize = " + msg.length + " message = " + new String(msg)) ; |
| this.sipStack.getStackLogger().logDebug("*******************\n"); |
| } |
| |
| } |
| 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 msg |
| * 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(byte[] msg, 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" + " messageSize = " + msg.length); |
| } |
| } |
| if (peerProtocol.compareToIgnoreCase("UDP") == 0) { |
| 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. |
| Socket outputSocket = sipStack.ioHandler.sendBytes( |
| this.messageProcessor.getIpAddress(), peerAddress, |
| peerPort, "tcp", msg, retry,this); |
| OutputStream myOutputStream = outputSocket.getOutputStream(); |
| myOutputStream.write(msg, 0, msg.length); |
| myOutputStream.flush(); |
| // The socket is cached (dont close it!); |
| } |
| } |
| |
| /** |
| * 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() { |
| } |
| |
| |
| } |