KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mr > core > net > IGDPortMapper


1 /*
2  * Copyright 2002 by
3  * <a HREF="http://www.coridan.com">Coridan</a>
4  * <a HREF="mailto: support@coridan.com ">support@coridan.com</a>
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with the
8  * License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is "MantaRay" (TM).
17  *
18  * The Initial Developer of the Original Code is Uri Schneider.
19  * Portions created by the Initial Developer are Copyright (C) 2006
20  * Coridan Inc. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source
23  * code where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LESSER GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above. If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LESSER GENERAL PUBLIC LICENSE.
34  
35  *
36  * This library is free software; you can redistribute it and/or modify it
37  * under the terms of the MPL as stated above or under the terms of the GNU
38  * Lesser General Public License as published by the Free Software Foundation;
39  * either version 2.1 of the License, or any later version.
40  *
41  * This library is distributed in the hope that it will be useful, but WITHOUT
42  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
43  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
44  * License for more details.
45  */

46
47 /**
48  * IGDPortMapper.java
49  *
50  * A singleton class, responsible for adding and removing port
51  * mappings from NAT devices which support the UPnP IGD specification.
52  *
53  * Created: Tue Sep 07 17:30:55 2004
54  *
55  * @author Uri Schneider
56  * @version 1.0
57  */

58
59 package org.mr.core.net;
60
61 import java.net.InetAddress JavaDoc;
62 import java.net.InetSocketAddress JavaDoc;
63 import java.util.HashMap JavaDoc;
64 import java.util.HashSet JavaDoc;
65 import java.util.Iterator JavaDoc;
66 import java.util.Map JavaDoc;
67
68 import org.apache.commons.logging.Log;
69 import org.apache.commons.logging.LogFactory;
70 import org.cybergarage.upnp.Action;
71 import org.cybergarage.upnp.ControlPoint;
72 import org.cybergarage.upnp.Device;
73 import org.cybergarage.upnp.DeviceList;
74 import org.cybergarage.upnp.Service;
75 import org.cybergarage.upnp.ServiceList;
76 import org.cybergarage.upnp.UPnPStatus;
77 import org.mr.MantaAgent;
78 import org.mr.core.configuration.ConfigManager;
79 import org.mr.core.util.SystemTime;
80 import org.mr.core.util.TimeoutTimer;
81 import org.mr.core.util.Timeoutable;
82
83 public class IGDPortMapper implements Timeoutable {
84     class Mapping {
85         private static final long DEFAULT_MIN_MAP_INTERVAL = 1000;
86         private static final long DEFAULT_REMAP_INTERVAL = 300000;
87
88         InetSocketAddress JavaDoc inner;
89         InetSocketAddress JavaDoc outer;
90         boolean tcp;
91         boolean pending;
92         long nextAction;
93         long interval;
94
95         Mapping(InetSocketAddress JavaDoc inner, InetSocketAddress JavaDoc outer, boolean tcp)
96         {
97             this.inner = inner;
98             this.outer = outer;
99             this.tcp = tcp;
100             this.pending = true;
101             this.nextAction = SystemTime.currentTimeMillis();
102             this.interval = DEFAULT_MIN_MAP_INTERVAL;
103         }
104
105         public boolean equals(Mapping other) {
106             return (this.inner.equals(other.inner) && this.tcp == other.tcp);
107         }
108         
109         /*
110          * the next method was added by lital kasif
111          */

112         public int hashCode(){
113             return this.inner.hashCode();
114         }
115         public int getLeaseDuration() {
116             return (int) (DEFAULT_REMAP_INTERVAL / 1000) * 2;
117         }
118
119         public boolean readyForAction() {
120             return SystemTime.currentTimeMillis() >= this.nextAction;
121         }
122
123         public void mappingSucceeded() {
124             this.pending = false;
125             this.nextAction = this.nextAction + DEFAULT_REMAP_INTERVAL;
126         }
127
128         public void mappingFailed() {
129             if (this.pending == false) {
130                 this.interval = DEFAULT_MIN_MAP_INTERVAL;
131             } else {
132                 this.interval = (long) ((double) this.interval * 1.7);
133             }
134             this.nextAction = this.nextAction + interval;
135         }
136
137         public String JavaDoc toString() {
138             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
139             buf.append(inner).append('/').append(outer).append('/');
140             buf.append(tcp ? "TCP" : "UDP");
141             return buf.toString();
142         }
143
144         public String JavaDoc dumpStatus() {
145             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
146             buf.append(toString());
147             buf.append(" [");
148             buf.append(pending ? "pending " : "");
149             buf.append(nextAction).append(' ');
150             buf.append(interval).append(']');
151             return buf.toString();
152         }
153     }
154
155     private ControlPoint cpoint;
156     private HashSet JavaDoc mappings;
157     private HashMap JavaDoc services;
158     private TimeoutTimer timer;
159     private Log log;
160     private boolean enabled;
161     private boolean infiniteLease;
162
163     private final static String JavaDoc WAN_CONNECTION_DEVICE =
164         "urn:schemas-upnp-org:device:WANConnectionDevice:1";
165     private final static String JavaDoc WAN_IP_SERVICE =
166         "urn:schemas-upnp-org:service:WANIPConnection:1";
167     private final static String JavaDoc WAN_PPP_SERVICE =
168         "urn:schemas-upnp-org:service:WANPPPConnection:1";
169     private final static long TIMER_INTERVAL = 1000;
170
171     private static IGDPortMapper instance = null;
172
173     public static synchronized IGDPortMapper getInstance() {
174         if (instance == null) {
175             instance = new IGDPortMapper();
176         }
177         return instance;
178     }
179
180     private IGDPortMapper() {
181         // if no config - this is probably a test app, so enable
182
this.enabled = true;
183         this.infiniteLease = false;
184         ConfigManager config = MantaAgent.getInstance().getSingletonRepository().getConfigManager();
185         if (config != null) {
186             this.enabled =
187                 config.getBooleanProperty("network.upnp.enabled", false);
188             this.infiniteLease =
189                 config.getBooleanProperty("network.upnp.infinite-lease", false);
190         }
191         
192         if (isEnabled()) {
193             this.cpoint = new ControlPoint();
194             this.mappings = new HashSet JavaDoc();
195             this.services = new HashMap JavaDoc();
196             this.timer = new TimeoutTimer("upnp",TIMER_INTERVAL, 1);
197             this.log = LogFactory.getLog("IGDPortMapper");
198 // this.cpoint.addSearchResponseListener(this);
199
this.cpoint.start();
200 // this.cpoint.search(ROOT_DEVICE);
201
// this.cpoint.search(WAN_CONNECTION_DEVICE);
202
this.timer.addTimeout(this, this, TIMER_INTERVAL);
203         }
204     } // IGDPortMapper constructor
205

206     /**
207      * Returns whether UPnP support is enabled. This is determined by
208      * the configuration variable 'net.upnp.enabled'.
209      *
210      * @return whether UPnP support is enabled.
211      */

212     public boolean isEnabled() {
213         return this.enabled;
214     }
215
216     /**
217      * This method will cause the mapper to search for a UPnP IGD
218      * device with the same external IP address as the 'outer'
219      * argument. If such device is found, a mapping will be added to
220      * it, as specified by the arguments.
221      *
222      * @param inner - the address of the local server socket (the one
223      * behind the NAT)
224      * @param outer - the IP address part of this argument is matched
225      * against the IGD's external address. The port is the external
226      * port of the mapping.
227      * @param tcp - whether this mapping refers to TCP or UDP ports.
228      */

229     public void addMapping(InetSocketAddress JavaDoc inner, InetSocketAddress JavaDoc outer,
230                            boolean tcp)
231     {
232         Mapping mapping = new Mapping(inner, outer, tcp);
233         this.mappings.add(mapping);
234         if(log.isDebugEnabled()){
235             log.debug("Mapping added " + mapping+".");
236         }
237     }
238
239     /**
240      * this will remove mapping from the mapper. It will not be
241      * renewed, and eventually expire.
242      *
243      * @param inner the mapping's inner address.
244      * @param tcp whether this was a TCP mapping or a UDP mapping.
245      */

246     public void removeMapping(InetSocketAddress JavaDoc inner, boolean tcp) {
247         Mapping mapping = new Mapping(inner, null, tcp);
248         this.mappings.remove(mapping);
249         if(log.isDebugEnabled()){
250             log.debug("Mapping removed " + mapping+".");
251         }
252     }
253
254 // public void deviceSearchResponseReceived(SSDPPacket packet) {
255
// String usn = packet.getUSN();
256
// String st = packet.getST();
257
// if (usn != null && st != null && st.equals(WAN_CONNECTION_DEVICE)) {
258
// Device dev = this.cpoint.getDevice(usn);
259
// if (dev != null) {
260
// ServiceList list = dev.getServiceList();
261
// }
262
// }
263
// }
264

265     public void timeout(Object JavaDoc event) {
266         housekeeper();
267         this.timer.addTimeout(this, this, TIMER_INTERVAL);
268     }
269
270     /**
271      * for debug: print all mappings with their status
272      */

273     public void dumpStatus() {
274         Iterator JavaDoc i = this.mappings.iterator();
275         System.out.println("Mappings");
276         System.out.println("--------");
277         while (i.hasNext()) {
278             System.out.println(((Mapping) i.next()).dumpStatus());
279         }
280         System.out.println();
281         System.out.println("Cached Services");
282         System.out.println("---------------");
283         i = this.services.entrySet().iterator();
284         while (i.hasNext()) {
285             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
286             System.out.print(entry.getKey().toString() + ": ");
287             System.out.println(entry.getValue().toString());
288         }
289         System.out.println();
290         System.out.println();
291     }
292
293     private void housekeeper() {
294         Iterator JavaDoc i = this.mappings.iterator();
295         while (i.hasNext()) {
296             Mapping mapping = (Mapping) i.next();
297             if (mapping.readyForAction()) {
298                 tryMapping(mapping);
299             }
300         }
301     }
302
303     private void tryMapping(Mapping mapping) {
304         Service service = findService(mapping.outer.getAddress());
305         if (service == null) {
306             if(log.isWarnEnabled()){
307                 log.warn("Could not create mapping " + mapping +
308                                  ": no device with specified address found.");
309             }
310             mapping.mappingFailed();
311         } else {
312             Action mapAction = service.getAction("AddPortMapping");
313             mapAction.setArgumentValue("NewRemoteHost", ""); // wildcard
314
mapAction.setArgumentValue("NewExternalPort",
315                                        "" + mapping.outer.getPort());
316             mapAction.setArgumentValue("NewProtocol",
317                                        mapping.tcp ? "TCP" : "UDP");
318             mapAction.setArgumentValue("NewInternalPort",
319                                        "" + mapping.inner.getPort());
320             mapAction.setArgumentValue("NewInternalClient",
321                                        "" + mapping.inner.getAddress().getHostAddress());
322             mapAction.setArgumentValue("NewEnabled", "1");
323             mapAction.setArgumentValue("NewPortMappingDescription", "Manta!");
324             int leaseDuration =
325                 (infiniteLease ? 0 : mapping.getLeaseDuration());
326             mapAction.setArgumentValue("NewLeaseDuration", "" + leaseDuration);
327
328             if (mapAction.postControlAction()) {
329                 mapping.mappingSucceeded();
330                 if(log.isDebugEnabled()){
331                     log.debug("Mapping succeeded " + mapping+".");
332                 }
333             } else {
334                 if(log.isWarnEnabled()){
335                     UPnPStatus err = mapAction.getStatus();
336                     log.warn("Could not create mapping " + mapping +
337                                      ": " +
338                                      UPnPStatus.code2String(err.getCode()) +
339                                      ": " + err.getDescription()+".");
340                 }
341                 mapping.mappingFailed();
342                 removeCachedService(mapping.outer.getAddress());
343             }
344         }
345     }
346
347     private Service findService(InetAddress JavaDoc address) {
348         Service service = (Service) this.services.get(address);
349         if (service == null) {
350             DeviceList devices = this.cpoint.getDeviceList();
351             for (int i = 0; i < devices.size(); i++) {
352                 Device rootDevice = devices.getDevice(i);
353                 Device wanDevice = rootDevice.getDevice(WAN_CONNECTION_DEVICE);
354                 if (wanDevice != null) {
355                     ServiceList serviceList = wanDevice.getServiceList();
356                     for (int j = 0; j < serviceList.size(); j++) {
357                         service = serviceList.getService(j);
358                         if (service.getServiceType().equals(WAN_IP_SERVICE) ||
359                             service.getServiceType().equals(WAN_PPP_SERVICE)) {
360                             cacheService(service);
361                         }
362                     }
363                 }
364             }
365         }
366
367         // now try again
368
return (Service) this.services.get(address);
369     }
370
371     /**
372      * @param service - assumed to be WAN{IP/PPP}ConnectionService
373      */

374     private void cacheService(Service service) {
375         Action action = service.getAction("GetExternalIPAddress");
376         if (action.postControlAction()) {
377             InetAddress JavaDoc addr;
378             String JavaDoc addrString =
379                 action.getArgumentValue("NewExternalIPAddress");
380             try {
381                 addr = InetAddress.getByName(addrString);
382                 this.services.put(addr, service);
383             } catch (Exception JavaDoc e) {
384                 // shouldn't happen
385
//System.out.println("IGD: " + e.getMessage());
386
log.error("IGD: " + e.getMessage());
387             }
388         } else {
389             if(log.isWarnEnabled()){
390                 log.warn("Could not get address from service" + service+".");
391             }
392         }
393     }
394
395     private void removeCachedService(InetAddress JavaDoc addr) {
396         this.services.remove(addr);
397     }
398 } // IGDPortMapper
399
Popular Tags