MediaProxy.java 8.7 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5 6 7 8 9 10
/**
 * $Revision$
 * $Date$
 *
 * Copyright (C) 2007 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

11 12
package org.jivesoftware.wildfire.mediaproxy;

Matt Tucker's avatar
Matt Tucker committed
13 14
import org.jivesoftware.util.Log;

15 16 17
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
18 19

/**
Matt Tucker's avatar
Matt Tucker committed
20 21 22 23 24 25 26
 * 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
27
 * with the MediaProxy(String localhost) constructor. <i>The media proxy ONLY works if you
Matt Tucker's avatar
Matt Tucker committed
28
 * are directly connected to the Internet with a valid IP address.</i>.
29 30
 *
 * @author Thiago Camargo
31 32 33
 */
public class MediaProxy implements SessionListener {

34
    final private Map<String, MediaProxySession> sessions = new ConcurrentHashMap<String, MediaProxySession>();
35 36 37 38 39 40

    private String localhost;

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

Thiago Camargo's avatar
Thiago Camargo committed
41
    private long idleTime = 60000;
42

43 44 45
    // Lifetime of a Channel in Seconds
    private long lifetime = 9000;

46
    /**
Matt Tucker's avatar
Matt Tucker committed
47
     * Contruct a MediaProxy instance that will listen on a specific network interface.
48
     *
Matt Tucker's avatar
Matt Tucker committed
49
     * @param localhost the IP of the locahost that will listen for packets.
50 51 52 53 54 55
     */
    public MediaProxy(String localhost) {
        this.localhost = localhost;
    }

    /**
56
     * Get the public IP of this media proxy that listen for incomming packets.
57
     *
Matt Tucker's avatar
Matt Tucker committed
58
     * @return the host that listens for incomming packets.
59 60 61 62 63 64
     */
    public String getPublicIP() {
        return localhost;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
65 66 67
     * 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. 
68
     *
Matt Tucker's avatar
Matt Tucker committed
69
     * @return the max idle time (in millis).
70
     */
Matt Tucker's avatar
Matt Tucker committed
71
    public long getIdleTime() {
Matt Tucker's avatar
Matt Tucker committed
72
        return idleTime;
73 74
    }

Thiago Camargo's avatar
Thiago Camargo committed
75
    /**
Matt Tucker's avatar
Matt Tucker committed
76 77 78
     * 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.
79
     *
Matt Tucker's avatar
Matt Tucker committed
80
     * @param idleTime the max idle time in millis.
81
     */
Matt Tucker's avatar
Matt Tucker committed
82 83
    public void setIdleTime(long idleTime) {
        this.idleTime = idleTime;
84 85 86
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
87
     * Returns the list of all currently active and running sessions.
88 89 90
     *
     * @return List of the Agents
     */
91 92
    public Collection<MediaProxySession> getSessions() {
        return sessions.values();
93 94 95
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
96
     * Returns the minimum port value to listen for incoming packets.
97
     *
Matt Tucker's avatar
Matt Tucker committed
98
     * @return the minimum port value.
99 100 101 102 103 104
     */
    public int getMinPort() {
        return minPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
105
     * Sets the minimum port value to listen from incoming packets.
106
     *
Matt Tucker's avatar
Matt Tucker committed
107
     * @param minPort the minimum port value.
108 109 110 111 112 113
     */
    public void setMinPort(int minPort) {
        this.minPort = minPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
114
     * Returns the maximum port value to listen for incoming packets.
115
     *
Matt Tucker's avatar
Matt Tucker committed
116
     * @return the maximun port value.
117 118 119 120 121 122
     */
    public int getMaxPort() {
        return maxPort;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
123
     * Sets the maximum port value to listen for incoming packets.
124
     *
Matt Tucker's avatar
Matt Tucker committed
125
     * @param maxPort the maximun port value.
126 127 128 129 130
     */
    public void setMaxPort(int maxPort) {
        this.maxPort = maxPort;
    }

131
    /**
Matt Tucker's avatar
Matt Tucker committed
132 133
     * Returns the maximum lifetime (in seconds) of a session. After the time period
     * elapses, the session will be destroyed even if currently active.
134
     * 
Matt Tucker's avatar
Matt Tucker committed
135
     * @return the max lifetime of a session (in seconds).
136 137 138 139 140 141
     */
    public long getLifetime() {
        return lifetime;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
142 143
     * Sets the maximum lifetime (in seconds) of a session. After the time period
     * elapses, the session will be destroyed even if currently active.
144
     *
Matt Tucker's avatar
Matt Tucker committed
145
     * @param lifetime the max lifetime of a session (in seconds).
146 147 148 149 150
     */
    public void setLifetime(long lifetime) {
        this.lifetime = lifetime;
    }

151
    /**
Matt Tucker's avatar
Matt Tucker committed
152
     * Returns a media proxy session with the specified ID.
153
     *
Matt Tucker's avatar
Matt Tucker committed
154 155
     * @param sid the session ID.
     * @return the session or <tt>null</tt> if the session doesn't exist.
156
     */
Matt Tucker's avatar
Matt Tucker committed
157
    public MediaProxySession getSession(String sid) {
158 159 160 161 162
        MediaProxySession proxySession = sessions.get(sid);
        if (proxySession != null) {
            if (Log.isDebugEnabled()) {
                Log.debug("SID: " + sid + " agentSID: " + proxySession.getSID());
                return proxySession;
163 164 165 166 167 168 169 170 171
            }
        }
        return null;
    }

    /**
     * Implements Session Listener stopAgent event.
     * Remove the stopped session from the sessions list.
     *
172
     * @param session the session that stopped
173
     */
Matt Tucker's avatar
Matt Tucker committed
174
    public void sessionClosed(MediaProxySession session) {
175 176 177 178
        sessions.remove(session.getSID());
        if (Log.isDebugEnabled()) {
            Log.debug("Session: " + session.getSID() + " removed.");
        }
179 180 181 182 183 184
    }

    /**
     * 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.
185 186
     * 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.
187
     * Every packet received from Point B will be relayed to the new Point A IP and port.
188 189
     * 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.
190 191 192
     * 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
193 194 195 196 197 198
     * @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
199 200
     * @return the added ProxyCandidate
     */
201
    public ProxyCandidate addRelayAgent(String id, String creator, String hostA, int portA,
Matt Tucker's avatar
Matt Tucker committed
202 203
            String hostB, int portB)
    {
204 205
        RelaySession session = new RelaySession(id, creator, localhost, hostA, portA, hostB, portB, minPort, maxPort);
        sessions.put(id, session);
Matt Tucker's avatar
Matt Tucker committed
206 207 208
        session.addKeepAlive(idleTime);
        session.addLifeTime(lifetime);
        session.addAgentListener(this);
209
        return session;
210 211 212 213 214
    }

    /**
     * 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.
215 216 217 218
     * 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.
219
     * Every packet received from Point B will be relayed to the new Point A IP and port.
220 221
     * 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.
222 223 224
     * 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
225
     * @param id id of the candidate returned (Could be a Jingle session ID)
Thiago Camargo's avatar
Thiago Camargo committed
226
     * @param creator the agent creator name or description
227 228
     * @return the added ProxyCandidate
     */
229 230
    public ProxyCandidate addRelayAgent(String id, String creator) {
        return addRelayAgent(id, creator, localhost, 40000, localhost, 40004);
231 232 233 234 235
    }

    /**
     * Stop every running sessions.
     */
236
    void stopProxy() {
Matt Tucker's avatar
Matt Tucker committed
237
        for (MediaProxySession session : getSessions()) {
238 239 240 241 242
            try {
                session.clearAgentListeners();
                session.stopAgent();
            }
            catch (Exception e) {
243
                Log.error("Error cleaning up media proxy sessions", e);
244 245 246 247
            }
        }
        sessions.clear();
    }
Matt Tucker's avatar
Matt Tucker committed
248
}