001/**
002 *
003 * Copyright 2003-2005 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingleold.nat;
018
019import java.net.Inet6Address;
020import java.net.InetAddress;
021import java.net.NetworkInterface;
022import java.net.SocketException;
023import java.util.Enumeration;
024import java.util.Random;
025import java.util.logging.Level;
026import java.util.logging.Logger;
027
028import org.jivesoftware.smack.SmackException;
029import org.jivesoftware.smack.SmackException.NoResponseException;
030import org.jivesoftware.smack.SmackException.NotConnectedException;
031import org.jivesoftware.smack.XMPPConnection;
032import org.jivesoftware.smack.XMPPException;
033import org.jivesoftware.smack.XMPPException.XMPPErrorException;
034
035import org.jivesoftware.smackx.jingleold.JingleSession;
036
037/**
038 * Bridged Resolver use a RTPBridge Service to add a relayed candidate.
039 * A very reliable solution for NAT Traversal.
040 *
041 * The resolver verify is the XMPP Server that the client is connected offer this service.
042 * If the server supports, a candidate is requested from the service.
043 * The resolver adds this candidate
044 */
045public class BridgedResolver extends TransportResolver {
046    private static final Logger LOGGER = Logger.getLogger(BridgedResolver.class.getName());
047
048    private final XMPPConnection connection;
049
050    private final Random random = new Random();
051
052    private long sid;
053
054    /**
055     * Constructor.
056     * A Bridged Resolver need an XMPPConnection to connect to a RTP Bridge.
057     *
058     * @param connection the XMPP connection.
059     */
060    public BridgedResolver(XMPPConnection connection) {
061        super();
062        this.connection = connection;
063    }
064
065    /**
066     * Resolve Bridged Candidate.
067     *
068     * The BridgedResolver takes the IP address and ports of a jmf proxy service.
069     * @throws NotConnectedException if the XMPP connection is not connected.
070     * @throws InterruptedException if the calling thread was interrupted.
071     */
072    @Override
073    public synchronized void resolve(JingleSession session) throws XMPPException, NotConnectedException, InterruptedException {
074
075        setResolveInit();
076
077        clearCandidates();
078
079        sid = random.nextInt(Integer.MAX_VALUE);
080
081        RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid));
082
083        String localIp = getLocalHost();
084
085        TransportCandidate localCandidate = new TransportCandidate.Fixed(
086                rtpBridge.getIp(), rtpBridge.getPortA());
087        localCandidate.setLocalIp(localIp);
088
089        TransportCandidate remoteCandidate = new TransportCandidate.Fixed(
090                rtpBridge.getIp(), rtpBridge.getPortB());
091        remoteCandidate.setLocalIp(localIp);
092
093        localCandidate.setSymmetric(remoteCandidate);
094        remoteCandidate.setSymmetric(localCandidate);
095
096        localCandidate.setPassword(rtpBridge.getPass());
097        remoteCandidate.setPassword(rtpBridge.getPass());
098
099        localCandidate.setSessionId(rtpBridge.getSid());
100        remoteCandidate.setSessionId(rtpBridge.getSid());
101
102        localCandidate.setConnection(this.connection);
103        remoteCandidate.setConnection(this.connection);
104
105        addCandidate(localCandidate);
106
107        setResolveEnd();
108    }
109
110    @Override
111    public void initialize() throws SmackException.SmackMessageException, XMPPErrorException, InterruptedException,
112                    NoResponseException, NotConnectedException {
113
114        clearCandidates();
115
116        if (!RTPBridge.serviceAvailable(connection)) {
117            setInitialized();
118            throw new SmackException.SmackMessageException("No RTP Bridge service available");
119        }
120        setInitialized();
121
122    }
123
124    @Override
125    public void cancel() throws XMPPException {
126        // Nothing to do here
127    }
128
129    public static String getLocalHost() {
130        Enumeration<NetworkInterface> ifaces = null;
131
132        try {
133            ifaces = NetworkInterface.getNetworkInterfaces();
134        }
135        catch (SocketException e) {
136            LOGGER.log(Level.WARNING, "exception", e);
137        }
138
139        while (ifaces.hasMoreElements()) {
140
141            NetworkInterface iface = ifaces.nextElement();
142            Enumeration<InetAddress> iaddresses = iface.getInetAddresses();
143
144            while (iaddresses.hasMoreElements()) {
145                InetAddress iaddress = iaddresses.nextElement();
146                if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress() && !(iaddress instanceof Inet6Address)) {
147                    return iaddress.getHostAddress();
148                }
149            }
150        }
151
152        try {
153            return InetAddress.getLocalHost().getHostAddress();
154        }
155        catch (Exception e) {
156            LOGGER.log(Level.WARNING, "exception", e);
157        }
158
159        return "127.0.0.1";
160
161    }
162
163}