Package com.ericsson.ssa.sip.dns

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

/*
* 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.sip.AddressImpl;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.RecordRouteResolver;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.ViaImpl;

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.SipURI;
import javax.servlet.sip.URI;


/**
* @reviewed ejoelbi 2007-jan-17
* @reviewed epetstr 2007-feb-20
*/
public class TargetResolver {
    private static TargetResolver myInstance = new TargetResolver();
    private static DnsResolver myDnsResolver;
    private static final int UNDEFINED_PORTNO = -1;
    // public static final int DEFAULT_RETRYTIME = 30;
    // public static final int DEFAULT_QUARANTINE_TIME = 1; // Changed from 30 to 1 sec -- moved to resolvermanager
    private final int MAX_MTU_SIZE_EXCL_VIA = UDPProtocol.MAX_MTU_SIZE - 50;
    private Logger logger = LogUtil.SIP_LOGGER.getLogger();

    private TargetResolver() {
    }

    public static TargetResolver getInstance() {
        if (myDnsResolver == null) {
            myDnsResolver = DnsResolver.getInstance();
        }
        return myInstance;
    }

    // TODO remove when TargetResolverTest is updated
    public void setFailed(TargetTuple target) {
        setTargetFailed(target, ResolverManager.getInstance().getDefaultQuarantineTime());
    }

    public void setTargetFailed(TargetTuple target, int expireValue) {
        myDnsResolver.setTargetFailed(target, expireValue);

        // A/AAAA/SRV:
        // The failure of a protocol/ip/port is stored for a while
        // to avoid unnecesary attempts to connect to a faulty host
        // a fail state is cleared after a configurable period
    }

    public TargetTuple resolveResponse(SipServletResponseImpl resp)
        throws Exception {
      String viaHeaderString = resp.getHeader(Header.VIA);
     
      if(viaHeaderString == null){
        throw new IllegalArgumentException("Unable to resolve response without Via header");
      }
        ViaImpl via = new ViaImpl(viaHeaderString);
        SipTransports transport = SipTransports.getTransport(via.getTransport());

        if (transport == SipTransports.UDP_PROT) {
             TargetTuple tt = resolveRespUDP(resp, via);
             RecordRouteResolver.resolveTransport(resp, tt);
             return tt;
        } else {
            TargetTuple tt = resolveRespTCP(resp, via, transport);
            RecordRouteResolver.resolveTransport(resp, tt);
            return tt;
        }
    }

    public TargetTuple resolveRespUDP(SipServletResponseImpl resp, ViaImpl via)
        throws Exception {
        SipTransports transport = SipTransports.UDP_PROT;

        // Check if maddr is present
        // 3261 18.2.2 bullet 2)
        String maddr = null;

        if ((maddr = via.getParameter(ViaImpl.PARAM_MADDR)) != null) {
            return resolveRespMaddr(maddr, via, transport);
        }

        // Check if "received" is present
        // 3261 18.2.2 bullet 3)
        String received = via.getParameter(ViaImpl.PARAM_RECEIVED);

        if (received != null) {
            return resolveRespReceived(received, via, transport);
        }

        // 3261 18.2.2 bullet 4)
        return resolveResp3263_5(via);
    }

    /**
     * 1) use already opened tcp channel found by transactionId 2) reopen the tcp
     * connection by means of (received, maddr, sent-by) 3) if reopen fails then
     * try alternate according to 3263 Chapter5
     */
    public TargetTuple resolveRespTCP(SipServletResponseImpl resp, ViaImpl via,
        SipTransports transport) throws Exception {
        // Check if "received" is present
        // 3261 18.2.2 bullet 1), second part
        String received = via.getParameter(ViaImpl.PARAM_RECEIVED);

        if (received != null) {
            return resolveRespReceived(received, via, transport);
        }

        // 3261 18.2.2 bullet 4)
        return resolveResp3263_5(via);
    }

    /**
     * Use this method if: - udp/received failed - tcp/received failed
     */
    public TargetTuple resolveRespAlternate(SipServletResponseImpl resp)
        throws Exception {
        ViaImpl via = new ViaImpl(resp.getHeader(Header.VIA));

        return resolveResp3263_5(via);
    }

    private TargetTuple resolveRespReceived(String received_ip, ViaImpl via,
        SipTransports transport) throws Exception {
        int port = via.getPort();
        String rport = null;

        if ((rport = via.getParameter(ViaImpl.PARAM_RPORT)) != null) {
            port = Integer.parseInt(rport);
        } else if (port == -1) {
            // use default
            port = transport.getDefaultPort();
        }

        // "received" MUST be numeric ip:
        // via-received = "received" EQUAL (IPv4address / IPv6address)
        // TODO see comments in OLDresolveUDPResponse()
        return new TargetTuple(transport, received_ip, port);
    }

    private TargetTuple resolveRespMaddr(String maddr, ViaImpl via,
        SipTransports transport) throws Exception {
        int port = via.getPort();

        if (port == -1) {
            // use default
            port = transport.getDefaultPort();
        }

        // maddr may be an domainname:
        // maddr-param = "maddr=" host
        if (isNumericIp(maddr) == false) {
            // A, AAAA lookup
            String ip = myDnsResolver.lookupName(maddr);

            if (ip == null) {
                throw new DnsLookupFailedException(
                    "Dns A lookup failed for host:" + maddr);
            }

            maddr = ip;
        }

        return new TargetTuple(transport, maddr, port);
    }

    public TargetTuple resolveResp3263_5(ViaImpl via) throws Exception {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "TargetResolver.resolve():\n VIA:" + via.toString() + "\n");
        }

        //
        SipTransports transport = SipTransports.getTransport(via.getTransport());

        // should we check MAX_MTU for resonses?
        // No where in 3261 it says so!! LETS NOT for time beeing
        if (transport.isSupported() == false) {
            throw new Exception("This transport is disabled in configuration: " +
                via.getTransport());
        }

        int port = via.getPort();
        String host = via.getHost();

        /*
         * Maddr SHOULD NOT be here? // rfc3261 18.2.2 handling // maddr handling
         * String maddr=via.getParameter(ViaImpl.PARAM_MADDR); if (maddr != null)
         * host = maddr;
         */
        if (isNumericIp(host)) {
            if (port == -1) {
                port = transport.getDefaultPort();
            }
        } else/* host is a domainname */
         {
            if (port != -1) {
                // if port is defined then
                // A, AAAA lookup
                String ip = myDnsResolver.lookupName(host);

                if (ip == null) {
                    throw new DnsLookupFailedException(
                        "Dns A lookup failed for host:" + host);
                }

                host = ip;
            } else {
                // if port is not defined in VIA
                // we should perform an SRV query
                TargetTuple target = null;

                if ((target = myDnsResolver.lookupSRVDirect(transport, host)) != null) {
                    return target;
                }

                throw new Exception(
                    "Failed to find a valid SRV record for via:" + via);
            }
        }

        return new TargetTuple(transport, host, port);
    }

    public TargetTuple resolveRequest(SipServletRequestImpl req, int msgSize)
        throws Exception {
        URI uri = null;
        String uriStr = null;

        if ((uriStr = req.getHeader(Header.ROUTE)) != null) {
            AddressImpl address = new AddressImpl(uriStr);
            uri = address.getURI();
        } else {
            uri = req.getRequestURI();
        }

        if (myDnsResolver.isTelephoneNumber(uri)) {
            SipURI sipuri = myDnsResolver.doLookupSipURI(uri);

            if (sipuri == null) {
                return null;
            }

            updateDefaultTransportParameter(sipuri);

            return resolveReq3263_4(sipuri, msgSize);
        } else if (uri.isSipURI()) {
            SipURI sipuri = (SipURI) uri;
            updateDefaultTransportParameter(sipuri);

            TargetTuple tt = resolveReq3263_4(sipuri, msgSize);
            RecordRouteResolver.resolveTransport(req, tt);
            return tt;
        } else {
            throw new Exception("Unknown type of uri :" + uri);
        }
    }

    /**
     * If {@link ResolverManager#isDefaultTCPTransport()} returns true then the
     * transport "transport=tcp" will be added to the URI if it is a SipURI.<br>
     *
     * @param uri
     *            This URI may be modified.
     */
    public void updateDefaultTransportParameter(URI uri) {
        if (!uri.isSipURI()) {
            return;
        }

        SipURI sipURI = (SipURI) uri;

        if (sipURI.isSecure() || (sipURI.getTransportParam() != null)) {
            return; //Already right transport
        }

        boolean isDefaultTCPTransport = ResolverManager.getInstance()
                                                       .isDefaultTCPTransport();

        if (isDefaultTCPTransport) {
            sipURI.setTransportParam(SipTransports.TCP_PROT.name());
        }
    }

    public TargetTuple resolveReq3263_4(SipURI sipuri, int msgSize)
        throws Exception {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE,
                "TargetResolver.resolve():\n URI:" + sipuri.toString() + "\n");
        }

        String host = sipuri.getHost();

        // maddr overides host 3263.4
        String maddr = sipuri.getMAddrParam();

        if (maddr != null) {
            host = maddr;
        }

        /*
         * if (sipuri.isSecure()) { throw new Exception("ASSERT Currently sips:
         * uri is not supported"); }
         */
        SipTransports theTransport = SipTransports.UNDEFINED_PROT;
        int thePort = sipuri.getPort();
        boolean isUdpMTUExceeded = false;

        if (msgSize > MAX_MTU_SIZE_EXCL_VIA) {
            isUdpMTUExceeded = true;
        }

        String transport = sipuri.getTransportParam();

        TargetTuple target = null;

        if (transport != null) {
            theTransport = SipTransports.getTransport(transport);
        } else if (isNumericIp(host) &&
                (theTransport == SipTransports.UNDEFINED_PROT)) {
            // if ("sips: uri")
            if (sipuri.isSecure()) {
                theTransport = SipTransports.TLS_PROT;
            } else {
                theTransport = SipTransports.UDP_PROT;
            }
        } else if (thePort != UNDEFINED_PORTNO) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                    "port specified in URI:" + sipuri.getPort());
            }

            if (theTransport == SipTransports.UNDEFINED_PROT) {
                theTransport = SipTransports.UDP_PROT;
            }
        } else if ((target = myDnsResolver.lookupNAPTRRecord(host,
                        isUdpMTUExceeded)) != null) {
            return target;
        } else if ((target = myDnsResolver.lookupSRVDirect(theTransport, host,
                        isUdpMTUExceeded)) != null) {
            return target;
        } else {
            // transport == null && port == null && no naptr && no srv
            // ---> SIP Default
            theTransport = SipTransports.UDP_PROT;
        }

        //--- Issue 1040
        // rfc3263. 4.2 Page 8-9
        if (!isNumericIp(host)) {
          if (thePort == UNDEFINED_PORTNO) {
            // Not numericIP && port is not defined.
            // SRV lookup
            target = myDnsResolver.lookupSRVDirect(theTransport, host,
                          isUdpMTUExceeded);
            if (target != null) {
              return target;
            }
          }
         
          // Not numericIp && port is defined.
          // ----OR---- previous lookup didn't get any result.
            // Do A, AAAA lookup
            String ip = myDnsResolver.lookupName(host);

            if (ip == null) {
                return null;
            }

            host = ip;
     
        }
       
        if (thePort == UNDEFINED_PORTNO) {
          // port is not defined.
            thePort = theTransport.getDefaultPort();
        }

        //--- END

        if ((theTransport == SipTransports.UDP_PROT) &&
                (isUdpMTUExceeded == true)) {
            theTransport = SipTransports.TCP_PROT;

            // RFC 3261 18.1.1 states that the caller must change "transport"
            // parameter accordingly
            // 8.1.1.7 says that a via is generated after the 3263 procedures
            // this means that it is not necesary to update the via according to
            // MTUExceeded protocol switch
            // TODO assign new "transport" value to
            // my URI...
            // to be used in the via header
            // as described in
            // RFC 3261 18.1.1
        }

        TargetTuple tuple = new TargetTuple(theTransport, host, thePort);

        if (myDnsResolver.isTargetFailed(tuple)) {
            return null;
        } else {
            return tuple;
        }
    }

    public static boolean isIPV6(String ipAddress) {
        // example URI with IPV6
        // <sip:ppessi@[3ffe::200:86ff:fe46:9616]:5066;maddr=[3ffe::200:86ff:fe46:9616]>
        return (ipAddress.charAt(0) == '[') && (ipAddress.indexOf(']') != -1);
    }

    /**
     * Checks whether "host" is an ipadress (either ipv4 or ipv6) or a domainname
     *
     * @param host
     *            String
     * @return boolean
     */
    public static boolean isNumericIp(String host) {
        // TODO fix this.....
        return Character.isDigit(host.charAt(0)) || (host.charAt(0) == '[');
    }
   
    /**
     * Updates via with recieved and rport information.
     * @param req where the VIA should be updated if needed
     */
    public static void updateVia(SipServletRequestImpl req, ViaImpl via) {
        // Update via with received+rport
    /**
      * This method is called only from the NetworkManager
      * and there is a check for Via there
      * Reusing the parsed via rather than create it.
      */
                                                                    // define
                                                                    // constants
                                                                    // somewhere

        String host = via.getHost();
        String packetSourceAddress = req.getRemote().getIP();
        boolean isViaModified = false;

        if (TargetResolver.isNumericIp(host)) {
            // rfc 3261 18.2.1 compare with packetsource address
            if (host.equals(packetSourceAddress) == false) {
                via.setParameter(ViaImpl.PARAM_RECEIVED, packetSourceAddress);
                isViaModified = true;
            }
        } else // host contains a domain name
         {
            via.setParameter(ViaImpl.PARAM_RECEIVED, packetSourceAddress);
            isViaModified = true;
        }

        String rport = via.getParameter(ViaImpl.PARAM_RPORT);

        if (rport != null) {
            via.setParameter(ViaImpl.PARAM_RPORT,
                new Integer(req.getRemote().getPort()).toString());
            // PARAM_RECEIVED may already be set above but, in that case we set
            // again with same value
            via.setParameter(ViaImpl.PARAM_RECEIVED, packetSourceAddress); // rfc
                                                                           // 3581 MUST always set received

            isViaModified = true;
        }

        if (isViaModified) {
            // update the "real" header with the changes above
            Header v = req.getRawHeader(Header.VIA);
            v.setReadOnly(false);

            ListIterator<String> it = v.getValues();
            it.next();
            it.set(via.toString());
            v.setReadOnly(true);
        }       
    }
}
TOP

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

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.