Package com.ericsson.ssa.container

Source Code of com.ericsson.ssa.container.OutboundFlowManager

package com.ericsson.ssa.container;

import java.net.InetSocketAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ListIterator;
import java.util.Set;

import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletMessage;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;

import org.jvnet.glassfish.comms.util.LogUtil;

import com.ericsson.ssa.config.annotations.Configuration;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.sip.AddressImpl;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.SipURIImpl;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetTuple;

/**
* Implementation of sip-outbound-15 rfc draft.
* @author Adrian Szwej
*
*/
public class OutboundFlowManager implements Layer {

    private static MessageDigest md;
   
    public static final String OUTBOUND_PARAM = "ob";
    public static final String GRUU_PARAM = "gr";
    public static final String SUPP_REQ_OUTBOUND_VALUE = "outbound";
    //can be omited to be anononynoues
    public static final String INSTANCE_ID_PARAM = "+sip.instance";
    public static final String REG_ID_PARAM = "reg-id";
   
    //flags
    public static final boolean OUTBOUND_SUPPORT_ENABLED = true;
   
    private Layer _nextLayer = null;
   
    private SipURIImpl m_URI = null;
    private SipURIImpl m_SURI = null;
    private boolean defaultTCPTransport = false;

    private static volatile OutboundFlowManager ofm = null;
    private static volatile boolean init = false;

    /** Access to the view containing the connection flows. */
    private Set<TargetTuple> _connectionView;
   
    public static OutboundFlowManager getInstance() {
        if (! init) {
            synchronized (OutboundFlowManager.class) {
                if (! init) {
                   ofm = new OutboundFlowManager();
                   init = true;
                }
            }
        }
        return ofm;
    }
   
    /**
     * Constructor.
     */
    public OutboundFlowManager() {
        establishURIs();
        SipBindingResolver.instance().registerSipBindingListener(new SipBindingListener() {
            public void newSipBindingCtxAvaliable(String context) {
                //ignore
            }
            public void sipBindingCtxUpdated(String context) {
                //ignore
            }
            public void publicSipBindingCtxUpdated() {
                    establishURIs();
            }
            public void sipBindingCtxRemoved(String context) {
                //ignore
            }
        });
    }
   
    public void next(SipServletRequestImpl req) {
        if (OUTBOUND_SUPPORT_ENABLED) {
            if (true/*isOutboundSupported(req)*/) { //TODO: required for all?
                processOutboundRequest(req);
            }
        } else {
            LayerHelper.next(req, this, _nextLayer);
        }
    }
   
    private boolean isRegisterOutbound(Header contactHeader) {
        try {
            if (contactHeader != null) {
                Address address = contactHeader.getAddressValue();
                if (address != null) {
                    String instanceId = address.getParameter(INSTANCE_ID_PARAM);
                    String regId = address.getParameter(REG_ID_PARAM);
                    if (instanceId != null) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } catch (ServletParseException e) {
        }
        return false;
    }

    /**
     * Inspects if we are the first hop node.
     * @param req
     * @return true if there is only one via present.
     */
    private boolean isFirstHop(SipServletRequestImpl req) {
        ListIterator<String> viaIterator = req.getHeaders(Header.VIA);
        if (viaIterator.hasNext()) {
            viaIterator.next(); //just swallow
           
            return !(viaIterator.hasNext());
        }
      
        return false;
    }
   
    /**
     * Checks if outbound is supported. Checks the required and supported header.
     * @param message request or response
     * @return true if outbound is required and supporte by the UA
     */
    private boolean isOutboundSupported(SipServletMessage message) {
        ListIterator<String> headerIter = message.getHeaders(Header.REQUIRE);
       
        while (headerIter.hasNext()) {
            if (headerIter.next().equals(SUPP_REQ_OUTBOUND_VALUE)) {
                return true;
            }
        }
       
        headerIter = message.getHeaders(Header.SUPPORTED);
        while (headerIter.hasNext()) {
            if (headerIter.next().equals(SUPP_REQ_OUTBOUND_VALUE)) {
                return true;
            }
        }
       
        return false;
    }   
   
    private void processOutboundRequest(SipServletRequestImpl req) {
        Header contactHeader = req.getRawHeader(Header.CONTACT);   

        if ("REGISTER".equals(req.getMethod())) {
            if (isFirstHop(req)) {
                if (isRegisterOutbound(contactHeader) && isOutboundSupported(req)) {
                    // add path header as RFC3327 so we are included in
                    // subsequent request to the UAC
                    addPathHeader(req);
                }
            }
            LayerHelper.next(req, this, _nextLayer);
        } else {
            Header routeHeader = req.getRawHeader(Header.ROUTE);
           
            if (isOutboundForwardIndicated(routeHeader)) {
                forwardRequest(req);
                   
            } else {
                LayerHelper.next(req, this, _nextLayer);
                //last possible place to find the outbound indicator
               /* if (isOutboundIndicated(req.getRequestURI())) {
                    //if its in request URI, then route header must exist otherwise we can't forward to flow
                    if (routeHeader != null)
                        forwardRequest(req);
                    else {
                        try {
                            req.createResponse(430).send();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                */
            }
        }
    }
   
    /**
     * Forwards the request to specified connection flow
     * @param req
     * @throws Exception
     */
    private void forward(SipServletRequestImpl req) throws Exception {
        //decrease max forward
        int maxForwards = req.getMaxForwards();
        req.setMaxForwards(--maxForwards);

        pushVia(req);
       
        // Alter connection and dispatch
        LayerHelper.resetDispatcher(req, NetworkManager.getInstance());
        req.popDispatcher().dispatch(req);
    }

    public void next(SipServletResponseImpl resp) {
        if (OUTBOUND_SUPPORT_ENABLED) {
            if (resp.getMethod().equals("REGISTER") && resp.getStatus() == 200) {
                //provide
                if (isOutboundSupported(resp)) {
                    String flowTimer = resp.getHeader(Header.FLOW_TIMER);
                    if (flowTimer != null) {
                        //connectionManager.provideKeepAlive(resp.getRemote(), Integer.parseInt(flowTimer));                   
                    } else {
                        //connectionManager.provideKeepAlive(resp.getRemote());
                    }
                }
                LayerHelper.next(resp, this, _nextLayer);
            } else {
              
                // Get topmost header
                Header viaHeader = resp.getRawHeader(Header.VIA);
               
                if (viaHeader != null) {
                    ListIterator<String> viaIterator;
                    viaIterator = viaHeader.getValues();
   
                    if (viaIterator.hasNext()) {
                        ViaImpl via = new ViaImpl(viaIterator.next());
                       
                        String outboundIndicated = via.getParameter(OUTBOUND_PARAM);
   
                        if (outboundIndicated != null) {
                            // This response was received from the back-end,
                            // pop Via, extract connection and forward it to the client.
                            viaHeader.setReadOnly(false);
                            viaIterator.remove();
                            viaHeader.setReadOnly(true);
   
   
                            // Alter connection and dispatch
                            LayerHelper.resetDispatcher(resp, NetworkManager.getInstance());
                            resp.popDispatcher().dispatch(resp); //will be resolved by via
                        }
                        LayerHelper.next(resp, this, _nextLayer);
                    }
                }
   
                // No Via? The response must be corrupt, drop it!
                //throw new SipRoutingException(
                //    "No Via on response, shall never happen; drop the response!");           
   
            }
        } else {
            LayerHelper.next(resp, this, _nextLayer);
        }
    }
   
    private void pushVia(SipServletRequestImpl request)  {
        Header viaHeader = request.getRawHeader(Header.VIA);
       
        ViaImpl topVia = null;
        String branchId;
        if (viaHeader == null) {
           
        } else {
            topVia = new ViaImpl(viaHeader.getValue());
            branchId = createBranchId(request, topVia);
            ViaImpl via = new ViaImpl("SIP", SipTransports.TCP_PROT.name(), getContainerAddress().toString());
            via.setParameter(OUTBOUND_PARAM, "");
           
            via.setParameter(ViaImpl.PARAM_BRANCH, branchId);
            viaHeader.setValue(via.toString(), true);
        }
    }

    /**
     * Checks if the request is oncoming or outgoing
     * @param recordRoute
     * @return
     */
    private boolean isRequestIncoming(Header routeHeader, TargetTuple remotePoint) {
        FlowToken flowToken = extractFlowToken(routeHeader);
        try {
            flowToken.validate();
           
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return true;
       
    }
   
    private boolean isOutboundIndicated(Header contactHeader) {
        try {
            URI uri = contactHeader.getAddressValue().getURI();
            return (isOutboundIndicated(uri, true) || false/*TODO: is GRUU*/);
        } catch (ServletParseException e) {
        }
        return false;
    }
   
    /**
     * Checks if there is any encoded connection information in the header indicated by the ob parameter
     * @param routeHeader
     * @return
     */
    private boolean isOutboundForwardIndicated(Header routeHeader) {
        try {
            return (routeHeader != null && isOutboundIndicated(routeHeader.getAddressValue().getURI(), false));
        } catch (ServletParseException e) {
        }
        return false;
    }   
   
    /**
     * Checks if outbound indicator exists in the URI.
     * @param uri the uri to check for the outbound parameter
     * @return true if there is an outbound parameter
     */
    private boolean isOutboundIndicated(URI uri, boolean gruuCheck) {
        if (uri.getParameter(OUTBOUND_PARAM) == null) {
            if (gruuCheck)
                return uri.getParameter(GRUU_PARAM) != null;
        } else {
            return true;
        }
       
        return false;
    }
   
    /**
     * Gets the address of this container and including the flowtoken as user part.
     * @param flowToken token to use for user prt-
     * @return address of this container
     */
    private SipURI getContainerAddress(FlowToken flowToken) {
        SipURIImpl sipURI = getContainerAddress();
        sipURI.setUser(flowToken.getEncodedFlowId());
        sipURI.setLrParam(true);

        return sipURI;
    }
   
    /**
     * Gets the address of this container.
     * @return address of this container
     */
    private SipURIImpl getContainerAddress() {
        SipURIImpl sipURI = getVipSipUri();
        return sipURI;
    }   
   
    /**
     * Ensures that the node stays in the path for incoming new call.
     */
    private void addPathHeader(SipServletRequestImpl req) {
        FlowToken flowToken = createFlowToken(req.getInitialRemote(), req.getLocal());
        SipURI sipURI = getContainerAddress(flowToken);
        sipURI.setParameter("ob", "");
        Header header = Header.createFormatted(Header.PATH, req);
        header.setValue("<" + sipURI.toString() + ">", false);
       
        req.addHeader(header);
    }  
   
    private FlowToken extractFlowToken(Header routeHeader) {
        FlowToken flowToken = null;
        if (routeHeader != null) {
            URI sipURI = null;
            try {
                sipURI = routeHeader.getAddressValue().getURI();
               
                if (sipURI instanceof SipURIImpl) {
                    String flowId = ((SipURIImpl)sipURI).getUser();
                   
                    flowToken = new FlowToken(flowId);
                }
            } catch (ServletParseException e) {
            }
        }
       
        return flowToken;
    }
   
    /**
     * Forwards the request stateless.
     * @param req, the request to forward stateless
     */
    private void forwardRequest(SipServletRequestImpl req)
    {
        //find the flow to forward the request
        Header routeHeader = req.getRawHeader(Header.ROUTE);
        FlowToken flowToken = extractFlowToken(routeHeader);

        if (flowToken != null) {
            try {
                flowToken.validate();
                TargetTuple flowTuple = flowToken.getRemote();

                //connectionView.getInstance().getConnectionView()
                if (getConnectioView().contains(flowTuple)) {
                    //remove the route header
                    Address poppedRoute = req.popRouteHeader();
                   
                    //remove if exists, exists for incoming dialog creational requests
                    poppedRoute.getURI().removeParameter(OUTBOUND_PARAM);
                    //we must check if there is already a value in route. eg put by clb.
                    if (SipFactoryImpl.isDialogCreational(req.getMethod())) {
                        //make sure we stay in the path
                        updateRecordRouteHeader(req, poppedRoute);
                    }
                   
                    req.setRemote(flowTuple);                   
                    forward(req);
                } else {
                    //flow does not exist, don't even try to reestablish
                    sendError(req, 430);   
                }
            } catch (Exception e) {
                sendError(req, 403);
            }
        }
    }
   
    /**
     * Updates the Record-Route if exists, otherwise creates a new one.
     * @param req
     * @return
     */
    private void updateRecordRouteHeader(SipServletRequestImpl req, Address poppedRoute) {
        Header recordRoute = req.getRawHeader(Header.RECORD_ROUTE);
        if (recordRoute == null) {
            recordRoute = Header.createFormatted(Header.RECORD_ROUTE, req);
            req.addHeader(recordRoute);
        }
       
        recordRoute.setValue('<' + poppedRoute.getURI().toString() + '>', true);
    }
   
    private static void sendError(SipServletRequestImpl req, int errorCode) {
        try {
            //outbound indicated, but flowtoken has been tampered
            req.createResponse(errorCode).send();
        } catch (Exception e1) {
            LogUtil.SIP_LOGGER.getLogger().warning("Cannot send response with errorcode: " + errorCode);
        }
    }
   
    /**
     * Creates a new flowtoken based on the connection information.
     *  
     * @return FlowToken object identifing the flow.
     */
    private static FlowToken createFlowToken(TargetTuple remotePoint, InetSocketAddress localPoint) {
        FlowToken flowToken = new FlowToken(remotePoint, localPoint);
        return flowToken;
    }

    public void addFlowOnRecordRoute(SipServletRequestImpl req) {
        Header contactHeader = req.getRawHeader(Header.CONTACT);   
        if (contactHeader != null && isOutboundIndicated(contactHeader)) {
               
            if (SipFactoryImpl.isDialogCreational(req.getMethod()) && isFirstHop(req)) {
                 addRecordRouteHeader(req);
            }
                   
        }
    }
   
    /**
     * Ensures that the node stays in the path for the call.
     */
    private void addRecordRouteHeader(SipServletRequestImpl req) {

        FlowToken flowToken = createFlowToken(req.getInitialRemote(), req.getLocal());
        SipURI sipURI = getContainerAddress(flowToken);
        Header header = req.getRawHeader(Header.RECORD_ROUTE);
       
        if (header == null) {
            header = Header.createFormatted(Header.RECORD_ROUTE, req);
            //add route so that any request passes this node
            req.addHeader(header);
        }
       
        header.setValue("<" + sipURI.toString() + ">", false);
    }
   
    /**
     * Gets the connection view once from the GrizzltNetworkManager
     * @return connection view.
     */
    private Set<TargetTuple> getConnectioView() {
        if (_connectionView == null) {
            synchronized (this) {
                if (_connectionView == null)
                    _connectionView = GrizzlyNetworkManager.getInstance().getConnectionView();
            }
        }
       
        return _connectionView;
    }
   
    public SipURIImpl getVipSipUri() {
        return (SipURIImpl) m_URI.clone();
    }
   
    public SipURIImpl getVipSipsUri() {
        return (SipURIImpl) m_SURI.clone();
    }

    /**
     * Sets the value for this parameter, this is done via reflection when
     * starting Layer is started.
     *
     * @param defaultTCPTransport
     *           true if TCP should be used false if UDP should be used.
     */
    @Configuration(key = "DefaultTcpTransport", node = "/SipService/SipProtocol")
    public void setDefaultTCPTransport(Boolean aDefaultTCPTransport) {
        defaultTCPTransport = aDefaultTCPTransport;
        establishURIs();
    }

    private void establishURIs() {

        m_URI = null;
        m_SURI = null;

        SipURIImpl[] ifs = SipBindingResolver.getInterfaces();

        //Find first UDP || TCP ||TLS
        for (int i = 0; i < ifs.length; i++) {
            if ((m_SURI == null) && ifs[i].isSecure()) {
                m_SURI = ifs[i];
            } else if ((m_URI == null) && defaultTCPTransport && ifs[i].getTransportParam().equalsIgnoreCase("TCP")) {
                m_URI = ifs[i];
            } else if ((m_URI == null) && !defaultTCPTransport && ifs[i].getTransportParam().equalsIgnoreCase("UDP")) {
                m_URI = ifs[i].clone();
                m_URI.removeParameter("transport");
            }
        }
    }
   
    /**
     * Create a branch ID
     * @param req the request
     * @param via the via to use for creating the branchId
     * @return the brancId
     */
    public static String createBranchId(SipServletRequestImpl req, ViaImpl via) {
        MessageDigest messageDigest = getMessageDigest();
        try {
            StringBuilder sb = new StringBuilder();
            sb.append(req.getRequestURI().toString());
            sb.append(req.getFromImpl().getParameter(AddressImpl.TAG_PARAM));
            sb.append(req.getCallId());
            sb.append(req.getCSeqNumber());
            sb.append(via.toString());

            MessageDigest localMD = (MessageDigest) messageDigest.clone();
            byte[] hash = localMD.digest(sb.toString().getBytes());

            sb = new StringBuilder();

            for (int i = 0; i < hash.length; i++) {
                String d = Integer.toHexString(new Byte(hash[i]).intValue() & 0xFF);

                if (d.length() == 1) {
                    sb.append('0');
                }

                sb.append(d);
            }

            return sb.toString();
        } catch (CloneNotSupportedException ex) {
           
        }
        return null;
    }
   
    private static MessageDigest getMessageDigest() throws Error {
        if (md == null) {
            try {
                md = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                // Should never happen!
                throw new Error(e);
            }
        }
       
        return md;
    }
   
    public void registerNext(Layer layer) {
        _nextLayer = layer;
    }

    public void setReporters(String reporters) {
    }

    public void dispatch(SipServletRequestImpl req) {
        req.popDispatcher().dispatch(req);
    }

    public void dispatch(SipServletResponseImpl resp) {
        resp.popDispatcher().dispatch(resp);
    }
   
    public Reporter getReporter() {
        return null;
    }
}
TOP

Related Classes of com.ericsson.ssa.container.OutboundFlowManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.