All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nist.javax.sip.stack.MessageChannel Maven / Gradle / Ivy

There is a newer version: 1.3.0-91
Show newest version
/*
 * 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 android.gov.nist.javax.sip.stack;

import android.gov.nist.core.CommonLogger;
import android.gov.nist.core.Host;
import android.gov.nist.core.HostPort;
import android.gov.nist.core.InternalErrorHandler;
import android.gov.nist.core.LogWriter;
import android.gov.nist.core.ServerLogger;
import android.gov.nist.core.StackLogger;
import android.gov.nist.javax.sip.ThreadAffinityTask;
import android.gov.nist.javax.sip.address.AddressImpl;
import android.gov.nist.javax.sip.header.ContentLength;
import android.gov.nist.javax.sip.header.ContentType;
import android.gov.nist.javax.sip.header.Via;
import android.gov.nist.javax.sip.message.MessageFactoryImpl;
import android.gov.nist.javax.sip.message.SIPMessage;
import android.gov.nist.javax.sip.message.SIPRequest;
import android.gov.nist.javax.sip.message.SIPResponse;

import java.io.IOException;
import java.net.InetAddress;
import java.text.ParseException;

import android.javax.sip.address.Hop;
import android.javax.sip.header.CSeqHeader;
import android.javax.sip.header.CallIdHeader;
import android.javax.sip.header.ContactHeader;
import android.javax.sip.header.ContentLengthHeader;
import android.javax.sip.header.ContentTypeHeader;
import android.javax.sip.header.FromHeader;
import android.javax.sip.header.ServerHeader;
import android.javax.sip.header.ToHeader;
import android.javax.sip.header.ViaHeader;

/**
 * Message channel abstraction for the SIP stack.
 *
 * @author M. Ranganathan 
Contains additions for support of symmetric NAT contributed by * Hagai. * * @version 1.2 $Revision: 1.40 $ $Date: 2010-12-02 22:44:53 $ * * */ public abstract class MessageChannel { private static StackLogger logger = CommonLogger.getLogger(MessageChannel.class); // 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; /** * The client transaction that this message channel points to. */ private SIPClientTransaction encapsulatedClientTransaction; /** * 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 for a viaHeader. */ 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(byte[] 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; } /** * Use to be able to report IOException in nonBlocking mode. */ static ThreadLocal messageTxId = new ThreadLocal(); /** * 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(final 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().equalsIgnoreCase(hop.getTransport())) { MessageChannel messageChannel = messageProcessor.createMessageChannel( hopAddr, hop.getPort()); if (messageChannel instanceof RawMessageChannel) { final RawMessageChannel channel = (RawMessageChannel) messageChannel; ThreadAffinityTask processMessageTask = new ThreadAffinityTask() { public void run() { try { ((RawMessageChannel) channel).processMessage((SIPMessage) sipMessage.clone()); } catch (Exception ex) { if (logger.isLoggingEnabled(ServerLogger.TRACE_ERROR)) { logger.logError("Error self routing message cause by: ", ex); } } } public Object getThreadHash() { return sipMessage.getCallId().getCallId(); } }; getSIPStack().getSelfRoutingThreadpoolExecutor().execute(processMessageTask); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) logger.logDebug("Self routing message"); return; } } } messageTxId.set(sipMessage.getTransactionId()); byte[] msg = sipMessage.encodeAsBytes(this.getTransport()); this.sendMessage(msg, hopAddr, hop.getPort(), sipMessage instanceof SIPRequest); // we successfully sent the message without an exception so let's // now set port and address sipMessage.setRemoteAddress(hopAddr); sipMessage.setRemotePort(hop.getPort()); sipMessage.setLocalPort(this.getPort()); sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress()); } catch (IOException ioe) { throw ioe; } catch (Exception ex) { if (this.logger.isLoggingEnabled(ServerLogger.TRACE_ERROR)) { this.logger.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 { messageTxId.remove(); if (this.logger.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(); byte[] bytes = sipMessage.encodeAsBytes(this.getTransport()); messageTxId.set(sipMessage.getTransactionId()); try { sendMessage(bytes, receiverAddress, receiverPort, sipMessage instanceof SIPRequest); } finally { messageTxId.remove(); } // we successfully sent the message without an exception so let's // set port and address before we feed it to the logger. sipMessage.setRemoteAddress(receiverAddress); sipMessage.setRemotePort(receiverPort); sipMessage.setLocalPort(this.getPort()); sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress()); //ready to log 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) { // http://java.net/jira/browse/JSIP-413 Concurrency issue within MessageChannel.java when using IPv6 addresses return (transport + ":" + inetAddr.getHostAddress().replaceAll("[\\[\\]]", "") + ":" + port).toLowerCase(); } /** * Generate a key given host and port. */ public static String getKey(HostPort hostPort, String transport) { // http://java.net/jira/browse/JSIP-413 Concurrency issue within MessageChannel.java when using IPv6 addresses String ipAddress = hostPort.getHost().getIpAddress(); if (ipAddress == null) { ipAddress = hostPort.getHost().getHostname(); } return (transport + ":" + ipAddress.replaceAll("[\\[\\]]", "") + ":" + 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. */ public void logMessage(SIPMessage sipMessage, InetAddress address, int port, long time) { if (!logger.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 String createBadReqRes(String badReq, ParseException pe) { StringBuilder buf = new StringBuilder(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()); } return buf.toString(); } /** * 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, StringBuilder 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, StringBuilder 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; } public SIPClientTransaction getEncapsulatedClientTransaction() { return this.encapsulatedClientTransaction; } public void setEncapsulatedClientTransaction(SIPClientTransaction transaction) { this.encapsulatedClientTransaction = transaction; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy