MediaProxy.java 9.26 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4
/**
 * $Revision$
 * $Date$
 *
5
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
6
 *
7 8 9 10 11 12 13 14 15 16 17
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
Matt Tucker's avatar
Matt Tucker committed
18 19
 */

20
package org.jivesoftware.openfire.mediaproxy;
21

22 23 24
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
25

26 27 28
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

29
/**
Matt Tucker's avatar
Matt Tucker committed
30 31 32 33 34 35 36
 * A Media Proxy relays UDP traffic between two IPs to provide connectivity between
 * two parties that are behind NAT devices. It also provides connectivity
 * between two parties that are directly connected to the internet or one party on the
 * internet and another behind a NAT.<p>
 *
 * Each connection relay between two parties is called a session. You can setup a MediaProxy
 * for all network interfaces with an empty constructor, or bind it to a specific interface
37
 * with the MediaProxy(String localhost) constructor. <i>The media proxy ONLY works if you
Matt Tucker's avatar
Matt Tucker committed
38
 * are directly connected to the Internet with a valid IP address.</i>.
39 40
 *
 * @author Thiago Camargo
41 42 43
 */
public class MediaProxy implements SessionListener {

44 45
	private static final Logger Log = LoggerFactory.getLogger(MediaProxy.class);

46
    final private Map<String, MediaProxySession> sessions = new ConcurrentHashMap<String, MediaProxySession>();
47

48
    private String ipAddress;
49 50 51 52

    private int minPort = 10000;
    private int maxPort = 20000;

Thiago Camargo's avatar
Thiago Camargo committed
53
    private long idleTime = 60000;
54

55 56 57
    // Lifetime of a Channel in Seconds
    private long lifetime = 9000;

58
    /**
Matt Tucker's avatar
Matt Tucker committed
59
     * Contruct a MediaProxy instance that will listen on a specific network interface.
60
     *
61
     * @param ipAddress the IP address on this server that will listen for packets.
62
     */
63 64
    public MediaProxy(String ipAddress) {
        this.ipAddress = ipAddress;
65 66 67
    }

    /**
68
     * Get the public IP of this media proxy that listen for incomming packets.
69
     *
Matt Tucker's avatar
Matt Tucker committed
70
     * @return the host that listens for incomming packets.
71 72
     */
    public String getPublicIP() {
73
        return ipAddress;
74 75 76
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
77 78 79
     * Returns the max time (in millis) that a session can remain open without
     * receiving any packets. After this time period elapses, the session is
     * automatically closed. 
80
     *
Matt Tucker's avatar
Matt Tucker committed
81
     * @return the max idle time (in millis).
82
     */
Matt Tucker's avatar
Matt Tucker committed
83
    public long getIdleTime() {
Matt Tucker's avatar
Matt Tucker committed
84
        return idleTime;
85 86
    }

Thiago Camargo's avatar
Thiago Camargo committed
87
    /**
Matt Tucker's avatar
Matt Tucker committed
88 89 90
     * Sets the max time (in millis) that a session can remain open without
     * receiving any packets. After this time period elapses, the session is
     * automatically closed.
91
     *
Matt Tucker's avatar
Matt Tucker committed
92
     * @param idleTime the max idle time in millis.
93
     */
Matt Tucker's avatar
Matt Tucker committed
94 95
    public void setIdleTime(long idleTime) {
        this.idleTime = idleTime;
96 97 98
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
99
     * Returns the list of all currently active and running sessions.
100 101 102
     *
     * @return List of the Agents
     */
103 104
    public Collection<MediaProxySession> getSessions() {
        return sessions.values();
105 106 107
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
108
     * Returns the minimum port value to listen for incoming packets.
109
     *
Matt Tucker's avatar
Matt Tucker committed
110
     * @return the minimum port value.
111 112 113 114 115 116
     */
    public int getMinPort() {
        return minPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
117
     * Sets the minimum port value to listen from incoming packets.
118
     *
Matt Tucker's avatar
Matt Tucker committed
119
     * @param minPort the minimum port value.
120 121 122 123 124 125
     */
    public void setMinPort(int minPort) {
        this.minPort = minPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
126
     * Returns the maximum port value to listen for incoming packets.
127
     *
Matt Tucker's avatar
Matt Tucker committed
128
     * @return the maximun port value.
129 130 131 132 133 134
     */
    public int getMaxPort() {
        return maxPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
135
     * Sets the maximum port value to listen for incoming packets.
136
     *
Matt Tucker's avatar
Matt Tucker committed
137
     * @param maxPort the maximun port value.
138 139 140 141 142
     */
    public void setMaxPort(int maxPort) {
        this.maxPort = maxPort;
    }

143
    /**
Matt Tucker's avatar
Matt Tucker committed
144 145
     * Returns the maximum lifetime (in seconds) of a session. After the time period
     * elapses, the session will be destroyed even if currently active.
146
     * 
Matt Tucker's avatar
Matt Tucker committed
147
     * @return the max lifetime of a session (in seconds).
148 149 150 151 152 153
     */
    public long getLifetime() {
        return lifetime;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
154 155
     * Sets the maximum lifetime (in seconds) of a session. After the time period
     * elapses, the session will be destroyed even if currently active.
156
     *
Matt Tucker's avatar
Matt Tucker committed
157
     * @param lifetime the max lifetime of a session (in seconds).
158 159 160 161 162
     */
    public void setLifetime(long lifetime) {
        this.lifetime = lifetime;
    }

163
    /**
Matt Tucker's avatar
Matt Tucker committed
164
     * Returns a media proxy session with the specified ID.
165
     *
Matt Tucker's avatar
Matt Tucker committed
166 167
     * @param sid the session ID.
     * @return the session or <tt>null</tt> if the session doesn't exist.
168
     */
Matt Tucker's avatar
Matt Tucker committed
169
    public MediaProxySession getSession(String sid) {
170 171 172
        MediaProxySession proxySession = sessions.get(sid);
        if (proxySession != null) {
            if (Log.isDebugEnabled()) {
173
                Log.debug("MediaProxy: SID: " + sid + " agentSID: " + proxySession.getSID());
174
                return proxySession;
175 176 177 178 179 180 181 182 183
            }
        }
        return null;
    }

    /**
     * Implements Session Listener stopAgent event.
     * Remove the stopped session from the sessions list.
     *
184
     * @param session the session that stopped
185
     */
Matt Tucker's avatar
Matt Tucker committed
186
    public void sessionClosed(MediaProxySession session) {
187 188
        sessions.remove(session.getSID());
        if (Log.isDebugEnabled()) {
189
            Log.debug("MediaProxy: Session: " + session.getSID() + " removed.");
190
        }
191 192 193 194 195 196
    }

    /**
     * Add a new Dynamic Session to the mediaproxy for defined IPs and ports.
     * The IP and port pairs can change depending of the Senders IP and port.
     * Which means that the IP and port values of the points can dynamic change after the Channel is opened.
197 198
     * When the agent receives a packet from Point A, the channel set the point A IP and port according to
     * the received packet sender IP and port.
199
     * Every packet received from Point B will be relayed to the new Point A IP and port.
200 201
     * When the agent receives a packet from Point B, the channel set the point B IP and port according to
     * the received packet sender IP and port.
202 203 204
     * Every packet received from Point A will be relayed to the new Point B IP and port.
     * Create a dynamic channel between two IPs. ( Dynamic Point A - Dynamic Point B )
     *
Thiago Camargo's avatar
Thiago Camargo committed
205 206 207 208 209 210
     * @param id      id of the candidate returned (Could be a Jingle session ID)
     * @param creator the agent creator name or description
     * @param hostA   the hostname or IP of the point A of the Channel
     * @param portA   the port number point A of the Channel
     * @param hostB   the hostname or IP of the point B of the Channel
     * @param portB   the port number point B of the Channel
211 212
     * @return the added ProxyCandidate
     */
213
    public ProxyCandidate addRelayAgent(String id, String creator, String hostA, int portA,
Matt Tucker's avatar
Matt Tucker committed
214 215
            String hostB, int portB)
    {
216
        RelaySession session = new RelaySession(id, creator, ipAddress, hostA, portA, hostB, portB, minPort, maxPort);
217
        sessions.put(id, session);
Matt Tucker's avatar
Matt Tucker committed
218 219 220
        session.addKeepAlive(idleTime);
        session.addLifeTime(lifetime);
        session.addAgentListener(this);
221
        return session;
222 223 224 225 226
    }

    /**
     * Add a new Dynamic Session to the mediaproxy WITHOUT defined IPs and ports.
     * The IP and port pairs WILL change depending of the Senders IP and port.
227 228 229 230
     * Which means that the IP and port values of the points will dynamic change after the Channel is opened
     * and received packet from both points.
     * When the agent receives a packet from Point A, the channel set the point A IP and port according to
     * the received packet sender IP and port.
231
     * Every packet received from Point B will be relayed to the new Point A IP and port.
232 233
     * When the agent receives a packet from Point B, the channel set the point B IP and port according to
     * the received packet sender IP and port.
234 235 236
     * Every packet received from Point A will be relayed to the new Point B IP and port.
     * Create a dynamic channel between two IPs. ( Dynamic Point A - Dynamic Point B )
     *
Matt Tucker's avatar
Matt Tucker committed
237
     * @param id id of the candidate returned (Could be a Jingle session ID)
Thiago Camargo's avatar
Thiago Camargo committed
238
     * @param creator the agent creator name or description
239 240
     * @return the added ProxyCandidate
     */
241
    public ProxyCandidate addRelayAgent(String id, String creator) {
242
        return addRelayAgent(id, creator, ipAddress, 40000, ipAddress, 40004);
243 244 245 246 247
    }

    /**
     * Stop every running sessions.
     */
248
    void stopProxy() {
Matt Tucker's avatar
Matt Tucker committed
249
        for (MediaProxySession session : getSessions()) {
250 251 252 253 254
            try {
                session.clearAgentListeners();
                session.stopAgent();
            }
            catch (Exception e) {
255
                Log.error("Error cleaning up media proxy sessions", e);
256 257 258 259
            }
        }
        sessions.clear();
    }
Matt Tucker's avatar
Matt Tucker committed
260
}