Package com.ericsson.ssa.sip.dns

Source Code of com.ericsson.ssa.sip.dns.ResolverManager

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip.dns;

import com.ericsson.ssa.config.annotations.Configuration;
import com.ericsson.ssa.config.annotations.UsagePolicy;
import com.ericsson.ssa.container.TransportNotSupportedException;
import com.ericsson.ssa.container.reporter.ReporterResolver;
import com.ericsson.ssa.container.reporter.Reporter;
import org.jvnet.glassfish.comms.deployment.backend.SessionCase;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.RecordRouteResolver;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.URIImpl;

import java.util.ListIterator;
import java.util.logging.Level;

// inserted by hockey (automatic)
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;

import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;


/**
* @author ekrigro TODO To change the template for this generated type comment
*         go to Window - Preferences - Java - Code Style - Code Templates
*
* @reviewed epetstr 2007-feb-20
*
*/
public class ResolverManager implements Layer {
    private static final int TIMEOUT_BASED_QUARANTINE_TIME = 0;
  private static final int DEFAULT_QUARANTINE_TIME = 1;
  private static DnsResolver myDnsResolver = DnsResolver.getInstance();
    private static ResolverManager myResolverManager = new ResolverManager();
    private static TargetResolver myTargetResolver = TargetResolver.getInstance();
    private Layer _nextLayer;
    private Logger logger = LogUtil.SIP_LOGGER.getLogger();
    private final int UNDEFINED_SIZE = -1; // messageLength for MTU_SIZE handling
    private boolean defaultTCPTransport;

    // Determines if the functionality described in RFC3263 4.3 Details of RFC
    // 2782 Process is disabled.
    private boolean _eas_503_disabled = false;
    private Reporter _reporter;
  private int _defaultQuarantineTime = DEFAULT_QUARANTINE_TIME;
  private int _timeoutBasedQuarantineTime = TIMEOUT_BASED_QUARANTINE_TIME;

    private ResolverManager() {
    }

    public void setReporters(String reporters) {
        _reporter = ReporterResolver.getInstance().getReporter(reporters);
    }

    public Reporter getReporter() {
        return _reporter;
    }

    public static ResolverManager getInstance() {
        return myResolverManager;
    }

    @Configuration(key="defaultQuarantineTime", node = "/SipContainer")
    public void setDefaultQuarantineTime(final Integer defaultQuarantineTime) {
        if (logger.isLoggable(Level.INFO)) {
            logger.log(Level.INFO, "default quarantine time value will be set to "
                    + defaultQuarantineTime.intValue());
        }
        if (defaultQuarantineTime == null) {
          _defaultQuarantineTime = DEFAULT_QUARANTINE_TIME;
        }
        _defaultQuarantineTime = defaultQuarantineTime.intValue();
    }
   
    public int getDefaultQuarantineTime() {
      return _defaultQuarantineTime;
    }
   
    @Configuration(key="timeoutBasedQuarantineTime", node = "/SipContainer")
    public void setTimeoutBasedQuarantineTime(final Integer timeoutBasedQuarantineTime) {
        if (logger.isLoggable(Level.INFO)) {
            logger.log(Level.INFO, "timeout based quarantine time value will be set to "
                    + timeoutBasedQuarantineTime.intValue());
        }
        if (timeoutBasedQuarantineTime == null) {
          _timeoutBasedQuarantineTime = TIMEOUT_BASED_QUARANTINE_TIME;
        }
        _timeoutBasedQuarantineTime = timeoutBasedQuarantineTime.intValue();
    }
   
    public int getTimeoutBasedQuarantineTime() {
      return _timeoutBasedQuarantineTime;
    }

    /**
     * Configurable parameter.
     *
     * @return true only if the {@link #setDefaultTCPTransport(boolean)} has been
     *         called with value true.
     */
    public synchronized boolean isDefaultTCPTransport() {
        return defaultTCPTransport;
    }

    /**
     * Called via java reflection from
     * <code> SSAContainerListener.initializeServer()</code>
     *
     * @param defaultTCPTransport
     *            true
     */
    @Configuration(key = "DefaultTcpTransport", node = "/SipService/SipProtocol")
    public synchronized void setDefaultTCPTransport(Boolean defaultTCPTransport) {
        this.defaultTCPTransport = defaultTCPTransport;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Layer#next(com.ericsson.ssa.sip.SipServletRequestImpl)
     */
    public void next(SipServletRequestImpl req) {
        // receiving a request...
        checkRouteHeader(req, true);

        req.pushTransactionDispatcher(this);
        req.pushApplicationDispatcher(this);
        LayerHelper.next(req, this, _nextLayer);
    }
   
    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Layer#next(com.ericsson.ssa.sip.SipServletResponseImpl)
     */
    public void next(SipServletResponseImpl resp) {
        boolean shouldForwardResponseToApplication = true;

        // if (resp.getStatus() == 503) // TODO check if we should use defined
        // identifiers (SC_SERVICE_UNAVAILABLE)
        // we do the dns failover for 503, no matter whether it was received from external or generated internally
        // but for timeout (408) we only do failover if it was generated by ourselves, not if received from the network.
        // Of course, if the dns failover is disabled (_eas_503_disabled) we skip the failover functionality
        if (!_eas_503_disabled
            && (resp.getStatus() == 503
                || (resp.getStatus() == 408 && resp.isInternalTransportFailure()))) {
          if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "handling DNS failover for : " + resp.getStatus());
          }
          int expireTime;
          // init the expireTime to a default which is overridden if we received
          // a retry-after in the response
          if (resp.getStatus() == 408) {
            // for timeouts use a different (typically longer) timer
            // the reason is that if we failed to reach an instance for 64*T1
            // we except that it might be more than a hiccup
            expireTime = getTimeoutBasedQuarantineTime();
          } else {
            expireTime = getDefaultQuarantineTime();
          }
          // check if the Retry-After header field is set&valid

            String retryAfterHeaderStr = resp.getHeader(Header.RETRY_AFTER);
            String retryAfter = getRetryAfterValue(retryAfterHeaderStr);

            if (retryAfter != null) {
                try {
                    expireTime = Integer.parseInt(retryAfter);
                } catch (NumberFormatException ignored) {
                }
            }

            try {
                TargetResolver resolver = TargetResolver.getInstance();
                SipServletRequestImpl req = (SipServletRequestImpl) resp.getRequest();
                // put the target in quarantine for the requested time
                if (expireTime > 0) {
                  resolver.setTargetFailed(req.getRemote(), expireTime);
                }
                // but for the current request we also mark it as failed
                // so regardless of the quarantine time it will not be tried twice
                // for the same request
                req.addFailedTarget(req.getRemote());
                // shortcut to let the dns resolver consider the failed targets
                // for this request by setting them via threadlocal
                DnsResolver.getInstance().setThreadLocalFailedTargets(req.getFailedTargets());
                TargetTuple newDestination = resolver.resolveRequest(req,
                        req.getMessageSize());

                if (newDestination == null) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE,
                            "List of available targets is exhausted");
                    }

                    shouldForwardResponseToApplication = true; // nothing more to
                                                               // do.....
                  resp.setInternalTransportFailure(false);
          if (resp.getStatus() == 408) {
              // keep the status
          } else {
              resp.setStatus(500);
          }
                } else {
                    req.setRemote(newDestination);
                    // send in new transaction....
                    req.restoreRetransmissionApplicationStack(
                                          ResolverManager.getInstance());
                    if("CANCEL".equals(req.getMethod())
                                        || "ACK".equals(req.getMethod())) {
                        if (logger.isLoggable(Level.FINE)) {
                            logger.log(Level.FINE,
                                "For CANCEL & ACK DONT POP VIA");
                        }
                    } else {
                        req.popTopVia();
                    }
                    req.popDispatcher().dispatch(req);
                    shouldForwardResponseToApplication = false; // Do not pass this
                                                                // response up
                }
            } catch (Exception e) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                        "failed during response processing:", e);
                }
            }
            finally {
              // good pratice to unset the threadlocal in a finally block
              DnsResolver.getInstance().clearThreadLocalFailedTargets();
            }
        }
        resp.setInternalTransportFailure(false); // also when dns failover is disabled
        if (shouldForwardResponseToApplication) {
            LayerHelper.next(resp, this, _nextLayer);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Layer#registerNext(com.ericsson.ssa.sip.Layer)
     */
    public void registerNext(Layer layer) {
        _nextLayer = layer;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Dispatcher#dispatch(com.ericsson.ssa.sip.SipServletRequestImpl)
     */
    public void dispatch(SipServletRequestImpl req) {
        // sending a request...

        //
        try {
            if (doResolve(req, req.getMessageSize())) {
                req.popDispatcher().dispatch(req);
            }
        } catch (TransportNotSupportedException noSuppExc) {
            /*
             * The size of a message must be checked after the transaction layer
             * has added the via header Therefore this RuntimeException is thrown
             * by network layer when messageSize > MAX_MTU_SIZE this means we tried
             * tcp instead of udp however remote host does not support tcp....
             */
            if (doResolve(req, UNDEFINED_SIZE)) {
                req.popDispatcher().dispatch(req);
            }
        }
    }

    private boolean doResolve(SipServletRequestImpl req, int msgSize) {
        TargetTuple destination = null;

        try {
            destination = myTargetResolver.resolveRequest(req, msgSize);
        } catch (Throwable t) {
            // log but ignore exception, destination will be null anyway...
            logger.log(Level.WARNING, "failed during request processing:", t);
        }

        if (destination == null) {
            handleResolvError(req);

            return false;
        }

        // hh29713, lets remove this loop detection since it causes problem when
        // spiral occurs.
        // jsr 289 will solve this.
        // Old correction removed: HG69331 Detect loop if ip-address points to us
        // and no more Route exist
        /*
         * if
         * (destination.getIP().equals(NetworkManager.getInstance().getPreferredIpAddress()) &&
         * (destination.getPort() == NetworkManager.getInstance().getDefaultPort() ||
         * destination.getPort() ==
         * NetworkManager.getInstance().getSubsequentPort()) &&
         * ((req.getHeader(Header.ROUTE)) == null)) { SipServletResponseImpl
         * loopResp = null; if (req.getMethod().equals("SUBSCRIBE") ||
         * req.getMethod().equals("NOTIFY")) { // RFC 3265: "489 Bad Event"
         * response should be returned to indicate that the // specified
         * event/event class is not understood by any application on this server
         * loopResp = req.createTerminatingResponse(489); } else { // return 404
         * Not Found loopResp = req.createTerminatingResponse(404); } if
         * (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "loop detected, send " +
         * loopResp.getStatus() + " response"); }
         * loopResp.popDispatcher().dispatch(loopResp); return false; }
         */
        req.setRemote(destination);

        req.handleAssertedIdentity();
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "resolving req:\n" + destination.toString());
        }

        return true;
    }

    private void handleResolvError(SipServletRequestImpl req) {
        SipServletResponseImpl failureResp = getRequestTimeout(req);

        // TR HH52078
        if (failureResp == null) {
            return;
        }

        // incoming req (with via) --> send 408 to client
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "failed to resolve req, send 408 response");
        }

        failureResp.popDispatcher().dispatch(failureResp);
    }

    private SipServletResponseImpl getRequestTimeout(SipServletRequestImpl in) {
        SipServletResponseImpl resp = in.createTerminatingResponse(408);

        // set the transaction request, since UA would have already
        // copied it... B2bUA will fail otherwise.
        SipServletRequestImpl req = in.getTransactionRequest();

        if ((req != null) && (resp != null)) {
            resp.setRequest(req);
            resp.setSession(req.getSessionImpl());
        }

        return resp;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Dispatcher#dispatch(com.ericsson.ssa.sip.SipServletResponseImpl)
     */
    public void dispatch(SipServletResponseImpl resp) {
        // sending a response...
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "sending response");
        }

        RecordRouteResolver.resolveTransport(resp);

        /*try {
            TargetTuple remote = null;
            remote = myTargetResolver.resolveResponse(resp);

            if (remote == null) {
                if (logger.isLoggable(Level.INFO)) {
                    logger.log(Level.INFO,
                        "Ignoring response. Cannot resolve target for:" + resp);

                    return;
                }
            }

            resp.setRemote(remote);
        } catch (Exception e) {
            // The only thing to do here is to ignore the response....
            logger.log(Level.INFO, "Exception when resolving via header:", e);

            return;
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "resolving via:\n" + remote.toString());
        }*/

        resp.popDispatcher().dispatch(resp);
    }

    public void checkRouteHeader(SipServletRequestImpl req, boolean remove) {
        // loose route support: remove first route header which should refer to
        // this sipstack
        // TODO implement support for strict route
        Header r = req.getRawHeader(Header.ROUTE);

        if (r == null) {
            req.setFragmentId(((URIImpl) req.getRequestURI()).getFragmentId());

            // Session Case handling
            req.setSessionCase(SessionCase.EXTERNAL);

            // end Session Case handling

            // Populate Parameters from Request URI if initial request
            if (req.isInitial()) {
                req.setRequestParams((URIImpl) req.getRequestURI());
            }
        } else {
            r.setReadOnly(false);

            ListIterator<Address> it_a = null;

            try {
                it_a = r.getAddressValues();
            } catch (ServletParseException e) {
                // TR HH52078
                SipServletResponseImpl failureResp = req.createTerminatingResponse(400);

                if (failureResp == null) {
                    return;
                }

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                        "failed to parse top route , send 400 response");
                }

                failureResp.popDispatcher().dispatch(failureResp);

                return;
            }

            Address a = it_a.next();
            URIImpl uri = (URIImpl) a.getURI();

            // Session Case handling
            // TODO move constants
            final String CALL_PARAM = "call";
            final String CALL_PARAM_ORIG = "orig";
            final String CALL_PARAM_TERMINATING = "term_registered";
            final String CALL_PARAM_TERMINATING_UNREG = "term_unregistered";

            String callParam = uri.getParameter(CALL_PARAM);

            // if(callParam == null) callParam = uri.getParameter(CALL_PARAM);
            if (callParam == null) {
                req.setSessionCase(SessionCase.EXTERNAL);
            } else {
                if (callParam.equals(CALL_PARAM_ORIG)) {
                    req.setSessionCase(SessionCase.ORIGINATING);
                } else if (callParam.equals(CALL_PARAM_TERMINATING)) {
                    req.setSessionCase(SessionCase.TERMINATING);
                } else if (callParam.equals(CALL_PARAM_TERMINATING_UNREG)) {
                    req.setSessionCase(SessionCase.TERMINATING_UNREGISTERED);
                } else {
                    req.setSessionCase(SessionCase.EXTERNAL);
                    logger.log(Level.WARNING,
                        "Assigned SessionCase external due to unknown value of " +
                        CALL_PARAM + " :" + callParam);
                }
            }

            // end Session Case handling
            if (uri.getLrParam()) {
                // Before removing the Route header extract the fragment-id.
                // This code needs BASIC TESTING in an extended
                // spiraling scenario, such as
                //
                // UAC/EAS --> proxy/external --> proxy/EAS --> UAS/external
                //
                // logger.log(Level.FINE, "+++ Route header with ;lr seen in resolver");
                // logger.log(Level.FINE, "+++ try setting frag info from the Route header
                // URI");
                // logger.log(Level.FINE, "+++ URI: "+(SipURIImpl)a.getURI());
                // logger.log(Level.FINE, "+++ and the id is:
                // "+((SipURIImpl)a.getURI()).getFragmentId());
                // logger.log(Level.FINE, "+++ let's set it in the request then");
                req.setFragmentId(((URIImpl) a.getURI()).getFragmentId());

                if (remove) {
                    if (logger.isLoggable(Level.FINE)) {
                        logger.log(Level.FINE, "Removing route = " + a);
                    }

                    it_a.remove();


                    // support of popped route added.
                    req.setInitialPoppedRoute(a);
                    req.setRecordRouteURI(uri);
                }
            } else {
                throw new IllegalAccessError(
                    "we do not support strict routes yet");
            }

            r.setReadOnly(true);
        }
    }

    private String getRetryAfterValue(String retry_after) {
        if (retry_after == null) {
            return null;
        }

        int stopIndex = 0;
        int length = retry_after.length();

        // Fixed defect HG89704 by checking length before doing charAt.
        while ((stopIndex < length) &&
                Character.isDigit(retry_after.charAt(stopIndex)))
            stopIndex++;

        return retry_after.substring(0, stopIndex);
    }

    @Configuration(key = "enum.top.domain", usage=UsagePolicy.IGNORE)
    public void setEnumTopDomain(String topDomain) {
        myDnsResolver.setEnumTopDomain(topDomain);
    }

    public String getEnumTopDomain() {
        return myDnsResolver.getEnumTopDomain();
    }

    /**
     * Returns whether the functionality described in RFC3263 4.3 Details of RFC
     * 2782 Process is disabled.
     *
     * @return
     */
    public Boolean isEas503Disabled() {
        return _eas_503_disabled;
    }

    /**
     * Sets whether the functionality described in RFC3263 4.3 Details of RFC
     * 2782 Process should be disabled.
     *
     * @param responseEnabled
     */
    @Configuration(key = "Eas503Disabled", node = "/SipService/SipProtocol", usage=UsagePolicy.IGNORE)
    public void setEas503Disabled(Boolean disabled) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "Eas503Disabled property is set to " + disabled);
        }

        _eas_503_disabled = disabled;
    }

    public synchronized void start() {
        /*defaultTCPTransport = Boolean.getBoolean(
                "sip.resolver.defaultTCPTransport");*/

        if (defaultTCPTransport) {
            logger.warning(
                "TCP default instead of UDP as in rfc3261, this option is for jsr116 TCK compatibility only.");
        }
    }

    /* Sets DNS cache size
     * Read from jvm-options like other dns properties
     */
    @Configuration(key = "dns.cache.size")
    public void setDnsCacheSize(int newSize)
    {
  myDnsResolver.setDnsCacheSize(newSize);
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.dns.ResolverManager

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.