예제 #1
0
public class UPnPPlugin
    implements Plugin,
        UPnPListener,
        UPnPMappingListener,
        UPnPWANConnectionListener,
        AEDiagnosticsEvidenceGenerator {
  private static final String UPNP_PLUGIN_CONFIGSECTION_ID = "UPnP";
  private static final String NATPMP_PLUGIN_CONFIGSECTION_ID = "NATPMP";

  private static final String STATS_DISCOVER = "discover";
  private static final String STATS_FOUND = "found";
  private static final String STATS_READ_OK = "read_ok";
  private static final String STATS_READ_BAD = "read_bad";
  private static final String STATS_MAP_OK = "map_ok";
  private static final String STATS_MAP_BAD = "map_bad";

  private static final String[] STATS_KEYS = {
    STATS_DISCOVER, STATS_FOUND, STATS_READ_OK, STATS_READ_BAD, STATS_MAP_OK, STATS_MAP_BAD
  };

  private PluginInterface plugin_interface;
  private LoggerChannel log;

  private UPnPMappingManager mapping_manager = UPnPMappingManager.getSingleton(this);

  private UPnP upnp;
  private UPnPLogListener upnp_log_listener;

  private NatPMPUPnP nat_pmp_upnp;

  private BooleanParameter natpmp_enable_param;
  private StringParameter nat_pmp_router;

  private BooleanParameter upnp_enable_param;
  private BooleanParameter trace_to_log;

  private BooleanParameter alert_success_param;
  private BooleanParameter grab_ports_param;
  private BooleanParameter alert_other_port_param;
  private BooleanParameter alert_device_probs_param;
  private BooleanParameter release_mappings_param;
  private StringParameter selected_interfaces_param;
  private StringParameter selected_addresses_param;

  private BooleanParameter ignore_bad_devices;
  private LabelParameter ignored_devices_list;

  private List<UPnPMapping> mappings = new ArrayList<UPnPMapping>();
  private List<UPnPPluginService> services = new ArrayList<UPnPPluginService>();

  private Map<URL, String> root_info_map = new HashMap<URL, String>();
  private Map<String, String> log_no_repeat_map = new HashMap<String, String>();

  protected AEMonitor this_mon = new AEMonitor("UPnPPlugin");

  public static void load(PluginInterface plugin_interface) {
    plugin_interface.getPluginProperties().setProperty("plugin.version", "1.0");
    plugin_interface
        .getPluginProperties()
        .setProperty("plugin.name", "Universal Plug and Play (UPnP)");
  }

  public void initialize(PluginInterface _plugin_interface) {
    plugin_interface = _plugin_interface;

    log = plugin_interface.getLogger().getTimeStampedChannel("UPnP");
    log.setDiagnostic();
    log.setForce(true);

    UIManager ui_manager = plugin_interface.getUIManager();

    final BasicPluginViewModel model = ui_manager.createBasicPluginViewModel("UPnP");
    model.setConfigSectionID(UPNP_PLUGIN_CONFIGSECTION_ID);

    BasicPluginConfigModel upnp_config =
        ui_manager.createBasicPluginConfigModel(
            ConfigSection.SECTION_PLUGINS, UPNP_PLUGIN_CONFIGSECTION_ID);

    // NATPMP

    BasicPluginConfigModel natpmp_config =
        ui_manager.createBasicPluginConfigModel(
            UPNP_PLUGIN_CONFIGSECTION_ID, NATPMP_PLUGIN_CONFIGSECTION_ID);

    natpmp_config.addLabelParameter2("natpmp.info");

    ActionParameter natpmp_wiki =
        natpmp_config.addActionParameter2("Utils.link.visit", "MainWindow.about.internet.wiki");

    natpmp_wiki.setStyle(ActionParameter.STYLE_LINK);

    natpmp_wiki.addListener(
        new ParameterListener() {
          public void parameterChanged(Parameter param) {
            try {
              plugin_interface.getUIManager().openURL(new URL("http://wiki.vuze.com/w/NATPMP"));

            } catch (Throwable e) {

              e.printStackTrace();
            }
          }
        });

    natpmp_enable_param =
        natpmp_config.addBooleanParameter2("natpmp.enable", "natpmp.enable", false);

    nat_pmp_router =
        natpmp_config.addStringParameter2("natpmp.routeraddress", "natpmp.routeraddress", "");

    natpmp_enable_param.addListener(
        new ParameterListener() {
          public void parameterChanged(Parameter param) {
            setNATPMPEnableState();
          }
        });

    natpmp_enable_param.addEnabledOnSelection(nat_pmp_router);

    // UPNP

    upnp_config.addLabelParameter2("upnp.info");
    upnp_config.addHyperlinkParameter2("upnp.wiki_link", "http://wiki.vuze.com/w/UPnP");

    upnp_enable_param = upnp_config.addBooleanParameter2("upnp.enable", "upnp.enable", true);

    grab_ports_param = upnp_config.addBooleanParameter2("upnp.grabports", "upnp.grabports", false);

    release_mappings_param =
        upnp_config.addBooleanParameter2("upnp.releasemappings", "upnp.releasemappings", true);

    ActionParameter refresh_param =
        upnp_config.addActionParameter2("upnp.refresh.label", "upnp.refresh.button");

    refresh_param.addListener(
        new ParameterListener() {
          public void parameterChanged(Parameter param) {
            UPnPPlugin.this.refreshMappings();
          }
        });

    // Auto-refresh mappings every minute when enabled.
    final BooleanParameter auto_refresh_on_bad_nat_param =
        upnp_config.addBooleanParameter2(
            "upnp.refresh_on_bad_nat", "upnp.refresh_mappings_on_bad_nat", false);
    plugin_interface
        .getUtilities()
        .createTimer("upnp mapping auto-refresh", true)
        .addPeriodicEvent(
            1 * 60 * 1000,
            new UTTimerEventPerformer() {
              private long last_bad_nat = 0;

              public void perform(UTTimerEvent event) {
                if (upnp == null) {
                  return;
                }
                if (!auto_refresh_on_bad_nat_param.getValue()) {
                  return;
                }
                if (!upnp_enable_param.getValue()) {
                  return;
                }
                int status = plugin_interface.getConnectionManager().getNATStatus();
                if (status == ConnectionManager.NAT_BAD) {
                  // Only try to refresh the mappings if this is the first bad NAT
                  // message we've been given in the last 15 minutes - we don't want
                  // to endlessly retry performing the mappings
                  long now = plugin_interface.getUtilities().getCurrentSystemTime();
                  if (last_bad_nat + (15 * 60 * 1000) < now) {
                    last_bad_nat = now;
                    log.log(
                        LoggerChannel.LT_WARNING,
                        "NAT status is firewalled - trying to refresh UPnP mappings");
                    refreshMappings(true);
                  }
                }
              }
            });

    upnp_config.addLabelParameter2("blank.resource");

    alert_success_param =
        upnp_config.addBooleanParameter2("upnp.alertsuccess", "upnp.alertsuccess", false);

    alert_other_port_param =
        upnp_config.addBooleanParameter2(
            "upnp.alertothermappings", "upnp.alertothermappings", true);

    alert_device_probs_param =
        upnp_config.addBooleanParameter2(
            "upnp.alertdeviceproblems", "upnp.alertdeviceproblems", true);

    selected_interfaces_param =
        upnp_config.addStringParameter2("upnp.selectedinterfaces", "upnp.selectedinterfaces", "");
    selected_addresses_param =
        upnp_config.addStringParameter2("upnp.selectedaddresses", "upnp.selectedaddresses", "");

    ignore_bad_devices =
        upnp_config.addBooleanParameter2("upnp.ignorebaddevices", "upnp.ignorebaddevices", true);

    ignored_devices_list = upnp_config.addLabelParameter2("upnp.ignorebaddevices.info");

    ActionParameter reset_param =
        upnp_config.addActionParameter2(
            "upnp.ignorebaddevices.reset", "upnp.ignorebaddevices.reset.action");

    reset_param.addListener(
        new ParameterListener() {
          public void parameterChanged(Parameter param) {
            PluginConfig pc = plugin_interface.getPluginconfig();

            for (int i = 0; i < STATS_KEYS.length; i++) {

              String key = "upnp.device.stats." + STATS_KEYS[i];

              pc.setPluginMapParameter(key, new HashMap());
            }

            pc.setPluginMapParameter("upnp.device.ignorelist", new HashMap());

            updateIgnoreList();
          }
        });

    trace_to_log =
        upnp_config.addBooleanParameter2("upnp.trace_to_log", "upnp.trace_to_log", false);

    final boolean enabled = upnp_enable_param.getValue();

    upnp_enable_param.addEnabledOnSelection(alert_success_param);
    upnp_enable_param.addEnabledOnSelection(grab_ports_param);
    upnp_enable_param.addEnabledOnSelection(refresh_param);
    upnp_enable_param.addEnabledOnSelection(alert_other_port_param);
    upnp_enable_param.addEnabledOnSelection(alert_device_probs_param);
    upnp_enable_param.addEnabledOnSelection(release_mappings_param);
    upnp_enable_param.addEnabledOnSelection(selected_interfaces_param);
    upnp_enable_param.addEnabledOnSelection(selected_addresses_param);
    upnp_enable_param.addEnabledOnSelection(ignore_bad_devices);
    upnp_enable_param.addEnabledOnSelection(ignored_devices_list);
    upnp_enable_param.addEnabledOnSelection(reset_param);
    upnp_enable_param.addEnabledOnSelection(trace_to_log);

    natpmp_enable_param.setEnabled(enabled);

    model.getStatus().setText(enabled ? "Running" : "Disabled");

    upnp_enable_param.addListener(
        new ParameterListener() {
          public void parameterChanged(Parameter p) {
            boolean e = upnp_enable_param.getValue();

            natpmp_enable_param.setEnabled(e);

            model.getStatus().setText(e ? "Running" : "Disabled");

            if (e) {

              startUp();

            } else {

              closeDown(true);
            }

            setNATPMPEnableState();
          }
        });

    model.getActivity().setVisible(false);
    model.getProgress().setVisible(false);

    log.addListener(
        new LoggerChannelListener() {
          public void messageLogged(int type, String message) {
            model.getLogArea().appendText(message + "\n");
          }

          public void messageLogged(String str, Throwable error) {
            model.getLogArea().appendText(error.toString() + "\n");
          }
        });

    // startup() used to be called on initializationComplete()
    // Moved to delayed task because rootDeviceFound can take
    // a lot of CPU cycle.  Let's hope nothing breaks
    DelayedTask dt =
        plugin_interface
            .getUtilities()
            .createDelayedTask(
                new Runnable() {
                  public void run() {
                    if (enabled) {

                      updateIgnoreList();

                      startUp();
                    }
                  }
                });
    dt.queue();

    plugin_interface.addListener(
        new PluginListener() {
          public void initializationComplete() {}

          public void closedownInitiated() {
            if (services.size() == 0) {

              plugin_interface.getPluginconfig().setPluginParameter("plugin.info", "");
            }
          }

          public void closedownComplete() {
            closeDown(true);
          }
        });
  }

  protected void updateIgnoreList() {
    try {
      String param = "";

      if (ignore_bad_devices.getValue()) {

        PluginConfig pc = plugin_interface.getPluginconfig();

        Map ignored = pc.getPluginMapParameter("upnp.device.ignorelist", new HashMap());

        Iterator it = ignored.entrySet().iterator();

        while (it.hasNext()) {

          Map.Entry entry = (Map.Entry) it.next();

          Map value = (Map) entry.getValue();

          param += "\n    " + entry.getKey() + ": " + new String((byte[]) value.get("Location"));
        }

        if (ignored.size() > 0) {

          log.log("Devices currently being ignored: " + param);
        }
      }

      String text =
          plugin_interface
              .getUtilities()
              .getLocaleUtilities()
              .getLocalisedMessageText("upnp.ignorebaddevices.info", new String[] {param});

      ignored_devices_list.setLabelText(text);

    } catch (Throwable e) {

      Debug.printStackTrace(e);
    }
  }

  protected void ignoreDevice(String USN, URL location) {
    // only take note of this if enabled to do so

    if (ignore_bad_devices.getValue()) {

      try {
        PluginConfig pc = plugin_interface.getPluginconfig();

        Map ignored = pc.getPluginMapParameter("upnp.device.ignorelist", new HashMap());

        Map entry = (Map) ignored.get(USN);

        if (entry == null) {

          entry = new HashMap();

          entry.put("Location", location.toString().getBytes());

          ignored.put(USN, entry);

          pc.setPluginMapParameter("upnp.device.ignorelist", ignored);

          updateIgnoreList();

          String text =
              plugin_interface
                  .getUtilities()
                  .getLocaleUtilities()
                  .getLocalisedMessageText(
                      "upnp.ignorebaddevices.alert", new String[] {location.toString()});

          log.logAlertRepeatable(LoggerChannel.LT_WARNING, text);
        }
      } catch (Throwable e) {

        Debug.printStackTrace(e);
      }
    }
  }

  protected void startUp() {
    if (upnp != null) {

      // already started up, must have been re-enabled

      refreshMappings();

      return;
    }

    final LoggerChannel core_log = plugin_interface.getLogger().getChannel("UPnP Core");

    try {
      upnp =
          UPnPFactory.getSingleton(
              new UPnPAdapter() {
                Set exception_traces = new HashSet();

                public SimpleXMLParserDocument parseXML(String data)
                    throws SimpleXMLParserDocumentException {
                  return (plugin_interface
                      .getUtilities()
                      .getSimpleXMLParserDocumentFactory()
                      .create(data));
                }

                public ResourceDownloaderFactory getResourceDownloaderFactory() {
                  return (plugin_interface.getUtilities().getResourceDownloaderFactory());
                }

                public UTTimer createTimer(String name) {
                  return (plugin_interface.getUtilities().createTimer(name, true));
                }

                public void createThread(String name, Runnable runnable) {
                  plugin_interface.getUtilities().createThread(name, runnable);
                }

                public Comparator getAlphanumericComparator() {
                  return (plugin_interface
                      .getUtilities()
                      .getFormatters()
                      .getAlphanumericComparator(true));
                }

                public void trace(String str) {
                  core_log.log(str);
                  if (trace_to_log.getValue()) {
                    upnp_log_listener.log(str);
                  }
                }

                public void log(Throwable e) {
                  String nested = Debug.getNestedExceptionMessage(e);

                  if (!exception_traces.contains(nested)) {

                    exception_traces.add(nested);

                    if (exception_traces.size() > 128) {

                      exception_traces.clear();
                    }

                    core_log.log(e);

                  } else {

                    core_log.log(nested);
                  }
                }

                public void log(String str) {
                  log.log(str);
                }

                public String getTraceDir() {
                  return (plugin_interface.getUtilities().getAzureusUserDir());
                }
              },
              getSelectedInterfaces());

      upnp.addRootDeviceListener(this);

      upnp_log_listener =
          new UPnPLogListener() {
            public void log(String str) {
              log.log(str);
            }

            public void logAlert(String str, boolean error, int type) {
              boolean logged = false;

              if (alert_device_probs_param.getValue()) {

                if (type == UPnPLogListener.TYPE_ALWAYS) {

                  log.logAlertRepeatable(
                      error ? LoggerChannel.LT_ERROR : LoggerChannel.LT_WARNING, str);

                  logged = true;

                } else {

                  boolean do_it = false;

                  if (type == UPnPLogListener.TYPE_ONCE_EVER) {

                    byte[] fp =
                        plugin_interface
                            .getUtilities()
                            .getSecurityManager()
                            .calculateSHA1(str.getBytes());

                    String key =
                        "upnp.alert.fp."
                            + plugin_interface
                                .getUtilities()
                                .getFormatters()
                                .encodeBytesToString(fp);

                    PluginConfig pc = plugin_interface.getPluginconfig();

                    if (!pc.getPluginBooleanParameter(key, false)) {

                      pc.setPluginParameter(key, true);

                      do_it = true;
                    }
                  } else {

                    do_it = true;
                  }

                  if (do_it) {

                    log.logAlert(error ? LoggerChannel.LT_ERROR : LoggerChannel.LT_WARNING, str);

                    logged = true;
                  }
                }
              }

              if (!logged) {

                log.log(str);
              }
            }
          };

      upnp.addLogListener(upnp_log_listener);

      mapping_manager.addListener(
          new UPnPMappingManagerListener() {
            public void mappingAdded(UPnPMapping mapping) {
              addMapping(mapping);
            }
          });

      UPnPMapping[] upnp_mappings = mapping_manager.getMappings();

      for (int i = 0; i < upnp_mappings.length; i++) {

        addMapping(upnp_mappings[i]);
      }

      setNATPMPEnableState();

    } catch (Throwable e) {

      log.log(e);
    }
  }

  protected void closeDown(final boolean end_of_day) {
    // problems here at end of day regarding devices that hang and cause AZ to hang around
    // got ages before terminating

    final AESemaphore sem = new AESemaphore("UPnPPlugin:closeTimeout");

    new AEThread("UPnPPlugin:closeTimeout", true) {
      public void runSupport() {
        try {
          for (int i = 0; i < mappings.size(); i++) {

            UPnPMapping mapping = (UPnPMapping) mappings.get(i);

            if (!mapping.isEnabled()) {

              continue;
            }

            for (int j = 0; j < services.size(); j++) {

              UPnPPluginService service = (UPnPPluginService) services.get(j);

              service.removeMapping(log, mapping, end_of_day);
            }
          }
        } finally {

          sem.release();
        }
      }
    }.start();

    if (!sem.reserve(end_of_day ? 15 * 1000 : 0)) {

      String msg =
          "A UPnP device is taking a long time to release its port mappings, consider disabling this via the UPnP configuration.";

      if (upnp_log_listener != null) {

        upnp_log_listener.logAlert(msg, false, UPnPLogListener.TYPE_ONCE_PER_SESSION);

      } else {

        log.logAlertRepeatable(LoggerChannel.LT_WARNING, msg);
      }
    }
  }

  public boolean deviceDiscovered(String USN, URL location) {
    String[] addresses = getSelectedAddresses();

    if (addresses.length > 0) {

      String address = location.getHost();

      boolean found = false;

      boolean all_exclude = true;

      for (int i = 0; i < addresses.length; i++) {

        String this_address = addresses[i];

        boolean include = true;

        if (this_address.startsWith("+")) {

          this_address = this_address.substring(1);

          all_exclude = false;

        } else if (this_address.startsWith("-")) {

          this_address = this_address.substring(1);

          include = false;

        } else {

          all_exclude = false;
        }

        if (this_address.equals(address)) {

          if (!include) {

            logNoRepeat(
                USN, "Device '" + location + "' is being ignored as excluded in address list");

            return (false);
          }

          found = true;

          break;
        }
      }

      if (!found) {

        if (all_exclude) {

          // if all exclude then we let others through
        } else {

          logNoRepeat(USN, "Device '" + location + "' is being ignored as not in address list");

          return (false);
        }
      }
    }

    if (!ignore_bad_devices.getValue()) {

      return (true);
    }

    incrementDeviceStats(USN, STATS_DISCOVER);

    boolean ok = checkDeviceStats(USN, location);

    String stats = "";

    for (int i = 0; i < STATS_KEYS.length; i++) {

      stats += (i == 0 ? "" : ",") + STATS_KEYS[i] + "=" + getDeviceStats(USN, STATS_KEYS[i]);
    }

    if (!ok) {

      logNoRepeat(USN, "Device '" + location + "' is being ignored: " + stats);

    } else {

      logNoRepeat(USN, "Device '" + location + "' is ok: " + stats);
    }

    return (ok);
  }

  protected void logNoRepeat(String usn, String msg) {
    synchronized (log_no_repeat_map) {
      String last = (String) log_no_repeat_map.get(usn);

      if (last != null && last.equals(msg)) {

        return;
      }

      log_no_repeat_map.put(usn, msg);
    }

    log.log(msg);
  }

  public void rootDeviceFound(UPnPRootDevice device) {
    incrementDeviceStats(device.getUSN(), "found");

    checkDeviceStats(device);

    try {
      int interesting = processDevice(device.getDevice());

      if (interesting > 0) {

        try {
          this_mon.enter();

          root_info_map.put(device.getLocation(), device.getInfo());

          Iterator<String> it = root_info_map.values().iterator();

          String all_info = "";

          List reported_info = new ArrayList();

          while (it.hasNext()) {

            String info = (String) it.next();

            if (info != null && !reported_info.contains(info)) {

              reported_info.add(info);

              all_info += (all_info.length() == 0 ? "" : ",") + info;
            }
          }

          if (all_info.length() > 0) {

            plugin_interface.getPluginconfig().setPluginParameter("plugin.info", all_info);
          }

        } finally {

          this_mon.exit();
        }
      }
    } catch (Throwable e) {

      log.log("Root device processing fails", e);
    }
  }

  protected boolean checkDeviceStats(UPnPRootDevice root) {
    return (checkDeviceStats(root.getUSN(), root.getLocation()));
  }

  protected boolean checkDeviceStats(String USN, URL location) {
    long discovers = getDeviceStats(USN, STATS_DISCOVER);
    long founds = getDeviceStats(USN, STATS_FOUND);

    if (discovers > 3 && founds == 0) {

      // discovered but never found - something went wrong with the device
      // construction process

      ignoreDevice(USN, location);

      return (false);

    } else if (founds > 0) {

      // found ok before, reset details in case now its screwed

      setDeviceStats(USN, STATS_DISCOVER, 0);
      setDeviceStats(USN, STATS_FOUND, 0);
    }

    long map_ok = getDeviceStats(USN, STATS_MAP_OK);
    long map_bad = getDeviceStats(USN, STATS_MAP_BAD);

    if (map_bad > 5 && map_ok == 0) {

      ignoreDevice(USN, location);

      return (false);

    } else if (map_ok > 0) {

      setDeviceStats(USN, STATS_MAP_OK, 0);
      setDeviceStats(USN, STATS_MAP_BAD, 0);
    }

    return (true);
  }

  protected long incrementDeviceStats(String USN, String stat_key) {
    String key = "upnp.device.stats." + stat_key;

    PluginConfig pc = plugin_interface.getPluginconfig();

    Map counts = pc.getPluginMapParameter(key, new HashMap());

    Long count = (Long) counts.get(USN);

    if (count == null) {

      count = new Long(1);

    } else {

      count = new Long(count.longValue() + 1);
    }

    counts.put(USN, count);

    pc.getPluginMapParameter(key, counts);

    return (count.longValue());
  }

  protected long getDeviceStats(String USN, String stat_key) {
    String key = "upnp.device.stats." + stat_key;

    PluginConfig pc = plugin_interface.getPluginconfig();

    Map counts = pc.getPluginMapParameter(key, new HashMap());

    Long count = (Long) counts.get(USN);

    if (count == null) {

      return (0);
    }

    return (count.longValue());
  }

  protected void setDeviceStats(String USN, String stat_key, long value) {
    String key = "upnp.device.stats." + stat_key;

    PluginConfig pc = plugin_interface.getPluginconfig();

    Map counts = pc.getPluginMapParameter(key, new HashMap());

    counts.put(USN, new Long(value));

    pc.getPluginMapParameter(key, counts);
  }

  public void mappingResult(UPnPWANConnection connection, boolean ok) {
    UPnPRootDevice root = connection.getGenericService().getDevice().getRootDevice();

    incrementDeviceStats(root.getUSN(), ok ? STATS_MAP_OK : STATS_MAP_BAD);

    checkDeviceStats(root);
  }

  public void mappingsReadResult(UPnPWANConnection connection, boolean ok) {
    UPnPRootDevice root = connection.getGenericService().getDevice().getRootDevice();

    incrementDeviceStats(root.getUSN(), ok ? STATS_READ_OK : STATS_READ_BAD);
  }

  protected String[] getSelectedInterfaces() {
    String si = selected_interfaces_param.getValue().trim();

    StringTokenizer tok = new StringTokenizer(si, ";");

    List res = new ArrayList();

    while (tok.hasMoreTokens()) {

      String s = tok.nextToken().trim();

      if (s.length() > 0) {

        res.add(s);
      }
    }

    return ((String[]) res.toArray(new String[res.size()]));
  }

  protected String[] getSelectedAddresses() {
    String si = selected_addresses_param.getValue().trim();

    StringTokenizer tok = new StringTokenizer(si, ";");

    List res = new ArrayList();

    while (tok.hasMoreTokens()) {

      String s = tok.nextToken().trim();

      if (s.length() > 0) {

        res.add(s);
      }
    }

    return ((String[]) res.toArray(new String[res.size()]));
  }

  protected int processDevice(UPnPDevice device) throws UPnPException {

    int interesting = processServices(device, device.getServices());

    UPnPDevice[] kids = device.getSubDevices();

    for (int i = 0; i < kids.length; i++) {

      interesting += processDevice(kids[i]);
    }

    return (interesting);
  }

  protected int processServices(UPnPDevice device, UPnPService[] device_services)
      throws UPnPException {
    int interesting = 0;

    for (int i = 0; i < device_services.length; i++) {

      UPnPService s = device_services[i];

      String service_type = s.getServiceType();

      if (service_type.equalsIgnoreCase("urn:schemas-upnp-org:service:WANIPConnection:1")
          || service_type.equalsIgnoreCase("urn:schemas-upnp-org:service:WANPPPConnection:1")) {

        final UPnPWANConnection wan_service = (UPnPWANConnection) s.getSpecificService();

        device
            .getRootDevice()
            .addListener(
                new UPnPRootDeviceListener() {
                  public void lost(UPnPRootDevice root, boolean replaced) {
                    removeService(wan_service, replaced);
                  }
                });

        addService(wan_service);

        interesting++;

      } else if (service_type.equalsIgnoreCase(
          "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {

        /* useless stats
        try{
        	UPnPWANCommonInterfaceConfig	config = (UPnPWANCommonInterfaceConfig)s.getSpecificService();

        	long[]	speeds = config.getCommonLinkProperties();

        	if ( speeds[0] > 0 && speeds[1] > 0 ){

        		log.log( "Device speed: down=" +
        					plugin_interface.getUtilities().getFormatters().formatByteCountToKiBEtcPerSec(speeds[0]/8) + ", up=" +
        					plugin_interface.getUtilities().getFormatters().formatByteCountToKiBEtcPerSec(speeds[1]/8));
        	}
        }catch( Throwable e ){

        	log.log(e);
        }
        */
      }
    }

    return (interesting);
  }

  protected void addService(UPnPWANConnection wan_service) throws UPnPException {

    wan_service.addListener(this);

    mapping_manager.serviceFound(wan_service);

    try {
      this_mon.enter();

      log.log(
          "    Found "
              + (wan_service.getGenericService().getServiceType().indexOf("PPP") == -1
                  ? "WANIPConnection"
                  : "WANPPPConnection"));

      UPnPWANConnectionPortMapping[] ports;

      String usn = wan_service.getGenericService().getDevice().getRootDevice().getUSN();

      if (getDeviceStats(usn, STATS_READ_OK) == 0 && getDeviceStats(usn, STATS_READ_BAD) > 2) {

        ports = new UPnPWANConnectionPortMapping[0];

        wan_service.periodicallyRecheckMappings(false);

        log.log("    Not reading port mappings from device due to previous failures");

      } else {

        ports = wan_service.getPortMappings();
      }

      for (int j = 0; j < ports.length; j++) {

        log.log(
            "      mapping ["
                + j
                + "] "
                + ports[j].getExternalPort()
                + "/"
                + (ports[j].isTCP() ? "TCP" : "UDP")
                + " ["
                + ports[j].getDescription()
                + "] -> "
                + ports[j].getInternalHost());
      }

      services.add(
          new UPnPPluginService(
              wan_service,
              ports,
              alert_success_param,
              grab_ports_param,
              alert_other_port_param,
              release_mappings_param));

      if (services.size() > 1) {

        // check this isn't a single device with multiple services

        String new_usn = wan_service.getGenericService().getDevice().getRootDevice().getUSN();

        boolean multiple_found = false;

        for (int i = 0; i < services.size() - 1; i++) {

          UPnPPluginService service = (UPnPPluginService) services.get(i);

          String existing_usn =
              service.getService().getGenericService().getDevice().getRootDevice().getUSN();

          if (!new_usn.equals(existing_usn)) {

            multiple_found = true;

            break;
          }
        }

        if (multiple_found) {

          PluginConfig pc = plugin_interface.getPluginconfig();

          if (!pc.getPluginBooleanParameter("upnp.device.multipledevices.warned", false)) {

            pc.setPluginParameter("upnp.device.multipledevices.warned", true);

            String text = MessageText.getString("upnp.alert.multipledevice.warning");

            log.logAlertRepeatable(LoggerChannel.LT_WARNING, text);
          }
        }
      }

      checkState();

    } finally {

      this_mon.exit();
    }
  }

  protected void removeService(UPnPWANConnection wan_service, boolean replaced) {
    try {
      this_mon.enter();

      String name =
          wan_service.getGenericService().getServiceType().indexOf("PPP") == -1
              ? "WANIPConnection"
              : "WANPPPConnection";

      String text =
          MessageText.getString(
              "upnp.alert.lostdevice",
              new String[] {
                name,
                wan_service.getGenericService().getDevice().getRootDevice().getLocation().getHost()
              });

      log.log(text);

      if ((!replaced) && alert_device_probs_param.getValue()) {

        log.logAlertRepeatable(LoggerChannel.LT_WARNING, text);
      }

      for (int i = 0; i < services.size(); i++) {

        UPnPPluginService ps = (UPnPPluginService) services.get(i);

        if (ps.getService() == wan_service) {

          services.remove(i);

          break;
        }
      }
    } finally {

      this_mon.exit();
    }
  }

  protected void addMapping(UPnPMapping mapping) {
    try {
      this_mon.enter();

      mappings.add(mapping);

      log.log("Mapping request: " + mapping.getString() + ", enabled = " + mapping.isEnabled());

      mapping.addListener(this);

      checkState();

    } finally {

      this_mon.exit();
    }
  }

  public void mappingChanged(UPnPMapping mapping) {
    checkState();
  }

  public void mappingDestroyed(UPnPMapping mapping) {
    try {
      this_mon.enter();

      mappings.remove(mapping);

      log.log("Mapping request removed: " + mapping.getString());

      for (int j = 0; j < services.size(); j++) {

        UPnPPluginService service = (UPnPPluginService) services.get(j);

        service.removeMapping(log, mapping, false);
      }
    } finally {

      this_mon.exit();
    }
  }

  protected void checkState() {
    try {
      this_mon.enter();

      for (int i = 0; i < mappings.size(); i++) {

        UPnPMapping mapping = (UPnPMapping) mappings.get(i);

        for (int j = 0; j < services.size(); j++) {

          UPnPPluginService service = (UPnPPluginService) services.get(j);

          service.checkMapping(log, mapping);
        }
      }
    } finally {

      this_mon.exit();
    }
  }

  public String[] getExternalIPAddresses() {
    List res = new ArrayList();

    try {
      this_mon.enter();

      for (int j = 0; j < services.size(); j++) {

        UPnPPluginService service = (UPnPPluginService) services.get(j);

        try {
          String address = service.getService().getExternalIPAddress();

          if (address != null) {

            res.add(address);
          }
        } catch (Throwable e) {

          Debug.printStackTrace(e);
        }
      }
    } finally {

      this_mon.exit();
    }

    return ((String[]) res.toArray(new String[res.size()]));
  }

  public UPnPPluginService[] getServices() {
    try {
      this_mon.enter();

      return ((UPnPPluginService[]) services.toArray(new UPnPPluginService[services.size()]));

    } finally {

      this_mon.exit();
    }
  }

  public UPnPPluginService[] getServices(UPnPDevice device) {
    String target_usn = device.getRootDevice().getUSN();

    List<UPnPPluginService> res = new ArrayList<UPnPPluginService>();

    try {
      this_mon.enter();

      for (UPnPPluginService service : services) {

        String this_usn =
            service.getService().getGenericService().getDevice().getRootDevice().getUSN();

        if (this_usn.equals(target_usn)) {

          res.add(service);
        }
      }
    } finally {

      this_mon.exit();
    }

    return (res.toArray(new UPnPPluginService[res.size()]));
  }

  // for external use, e.g. webui

  public UPnPMapping addMapping(String desc_resource, boolean tcp, int port, boolean enabled) {
    return (mapping_manager.addMapping(desc_resource, tcp, port, enabled));
  }

  public UPnPMapping getMapping(boolean tcp, int port) {
    return (mapping_manager.getMapping(tcp, port));
  }

  public UPnPMapping[] getMappings() {
    return (mapping_manager.getMappings());
  }

  public boolean isEnabled() {
    return (upnp_enable_param.getValue());
  }

  protected void setNATPMPEnableState() {
    boolean enabled = natpmp_enable_param.getValue() && upnp_enable_param.getValue();

    try {
      if (enabled) {

        if (nat_pmp_upnp == null) {

          nat_pmp_upnp =
              NatPMPUPnPFactory.create(
                  upnp,
                  NatPMPDeviceFactory.getSingleton(
                      new NATPMPDeviceAdapter() {
                        public String getRouterAddress() {
                          return (nat_pmp_router.getValue());
                        }

                        public void log(String str) {
                          log.log("NAT-PMP: " + str);
                        }
                      }));

          nat_pmp_upnp.addListener(this);
        }

        nat_pmp_upnp.setEnabled(true);
      } else {

        if (nat_pmp_upnp != null) {

          nat_pmp_upnp.setEnabled(false);
        }
      }
    } catch (Throwable e) {

      log.log("Failed to initialise NAT-PMP subsystem", e);
    }
  }

  protected void logAlert(int type, String resource, String[] params) {
    String text =
        plugin_interface
            .getUtilities()
            .getLocaleUtilities()
            .getLocalisedMessageText(resource, params);

    log.logAlertRepeatable(type, text);
  }

  /** Provided for use by other plugins. */
  public void refreshMappings() {
    refreshMappings(false);
  }

  /** Provided for use by other plugins. */
  public void refreshMappings(boolean force) {
    if (force) {
      closeDown(true);
      startUp();
    } else {
      this.upnp.reset();
    }
  }

  public void generate(IndentWriter writer) {
    List<UPnPMapping> mappings_copy;
    List<UPnPPluginService> services_copy;

    try {
      this_mon.enter();

      mappings_copy = new ArrayList<UPnPMapping>(mappings);

      services_copy = new ArrayList<UPnPPluginService>(services);

    } finally {

      this_mon.exit();
    }

    writer.println("Mappings");

    try {
      writer.indent();

      for (UPnPMapping mapping : mappings_copy) {

        if (mapping.isEnabled()) {

          writer.println(mapping.getString());
        }
      }
    } finally {

      writer.exdent();
    }

    writer.println("Services");

    try {
      writer.indent();

      for (UPnPPluginService service : services_copy) {

        writer.println(service.getString());
      }
    } finally {

      writer.exdent();
    }
  }
}
예제 #2
0
  protected void startUp() {
    if (upnp != null) {

      // already started up, must have been re-enabled

      refreshMappings();

      return;
    }

    final LoggerChannel core_log = plugin_interface.getLogger().getChannel("UPnP Core");

    try {
      upnp =
          UPnPFactory.getSingleton(
              new UPnPAdapter() {
                Set exception_traces = new HashSet();

                public SimpleXMLParserDocument parseXML(String data)
                    throws SimpleXMLParserDocumentException {
                  return (plugin_interface
                      .getUtilities()
                      .getSimpleXMLParserDocumentFactory()
                      .create(data));
                }

                public ResourceDownloaderFactory getResourceDownloaderFactory() {
                  return (plugin_interface.getUtilities().getResourceDownloaderFactory());
                }

                public UTTimer createTimer(String name) {
                  return (plugin_interface.getUtilities().createTimer(name, true));
                }

                public void createThread(String name, Runnable runnable) {
                  plugin_interface.getUtilities().createThread(name, runnable);
                }

                public Comparator getAlphanumericComparator() {
                  return (plugin_interface
                      .getUtilities()
                      .getFormatters()
                      .getAlphanumericComparator(true));
                }

                public void trace(String str) {
                  core_log.log(str);
                  if (trace_to_log.getValue()) {
                    upnp_log_listener.log(str);
                  }
                }

                public void log(Throwable e) {
                  String nested = Debug.getNestedExceptionMessage(e);

                  if (!exception_traces.contains(nested)) {

                    exception_traces.add(nested);

                    if (exception_traces.size() > 128) {

                      exception_traces.clear();
                    }

                    core_log.log(e);

                  } else {

                    core_log.log(nested);
                  }
                }

                public void log(String str) {
                  log.log(str);
                }

                public String getTraceDir() {
                  return (plugin_interface.getUtilities().getAzureusUserDir());
                }
              },
              getSelectedInterfaces());

      upnp.addRootDeviceListener(this);

      upnp_log_listener =
          new UPnPLogListener() {
            public void log(String str) {
              log.log(str);
            }

            public void logAlert(String str, boolean error, int type) {
              boolean logged = false;

              if (alert_device_probs_param.getValue()) {

                if (type == UPnPLogListener.TYPE_ALWAYS) {

                  log.logAlertRepeatable(
                      error ? LoggerChannel.LT_ERROR : LoggerChannel.LT_WARNING, str);

                  logged = true;

                } else {

                  boolean do_it = false;

                  if (type == UPnPLogListener.TYPE_ONCE_EVER) {

                    byte[] fp =
                        plugin_interface
                            .getUtilities()
                            .getSecurityManager()
                            .calculateSHA1(str.getBytes());

                    String key =
                        "upnp.alert.fp."
                            + plugin_interface
                                .getUtilities()
                                .getFormatters()
                                .encodeBytesToString(fp);

                    PluginConfig pc = plugin_interface.getPluginconfig();

                    if (!pc.getPluginBooleanParameter(key, false)) {

                      pc.setPluginParameter(key, true);

                      do_it = true;
                    }
                  } else {

                    do_it = true;
                  }

                  if (do_it) {

                    log.logAlert(error ? LoggerChannel.LT_ERROR : LoggerChannel.LT_WARNING, str);

                    logged = true;
                  }
                }
              }

              if (!logged) {

                log.log(str);
              }
            }
          };

      upnp.addLogListener(upnp_log_listener);

      mapping_manager.addListener(
          new UPnPMappingManagerListener() {
            public void mappingAdded(UPnPMapping mapping) {
              addMapping(mapping);
            }
          });

      UPnPMapping[] upnp_mappings = mapping_manager.getMappings();

      for (int i = 0; i < upnp_mappings.length; i++) {

        addMapping(upnp_mappings[i]);
      }

      setNATPMPEnableState();

    } catch (Throwable e) {

      log.log(e);
    }
  }
예제 #3
0
 public UPnPMapping[] getMappings() {
   return (mapping_manager.getMappings());
 }
예제 #4
0
 public UPnPMapping getMapping(boolean tcp, int port) {
   return (mapping_manager.getMapping(tcp, port));
 }
예제 #5
0
 public UPnPMapping addMapping(String desc_resource, boolean tcp, int port, boolean enabled) {
   return (mapping_manager.addMapping(desc_resource, tcp, port, enabled));
 }
예제 #6
0
  protected void addService(UPnPWANConnection wan_service) throws UPnPException {

    wan_service.addListener(this);

    mapping_manager.serviceFound(wan_service);

    try {
      this_mon.enter();

      log.log(
          "    Found "
              + (wan_service.getGenericService().getServiceType().indexOf("PPP") == -1
                  ? "WANIPConnection"
                  : "WANPPPConnection"));

      UPnPWANConnectionPortMapping[] ports;

      String usn = wan_service.getGenericService().getDevice().getRootDevice().getUSN();

      if (getDeviceStats(usn, STATS_READ_OK) == 0 && getDeviceStats(usn, STATS_READ_BAD) > 2) {

        ports = new UPnPWANConnectionPortMapping[0];

        wan_service.periodicallyRecheckMappings(false);

        log.log("    Not reading port mappings from device due to previous failures");

      } else {

        ports = wan_service.getPortMappings();
      }

      for (int j = 0; j < ports.length; j++) {

        log.log(
            "      mapping ["
                + j
                + "] "
                + ports[j].getExternalPort()
                + "/"
                + (ports[j].isTCP() ? "TCP" : "UDP")
                + " ["
                + ports[j].getDescription()
                + "] -> "
                + ports[j].getInternalHost());
      }

      services.add(
          new UPnPPluginService(
              wan_service,
              ports,
              alert_success_param,
              grab_ports_param,
              alert_other_port_param,
              release_mappings_param));

      if (services.size() > 1) {

        // check this isn't a single device with multiple services

        String new_usn = wan_service.getGenericService().getDevice().getRootDevice().getUSN();

        boolean multiple_found = false;

        for (int i = 0; i < services.size() - 1; i++) {

          UPnPPluginService service = (UPnPPluginService) services.get(i);

          String existing_usn =
              service.getService().getGenericService().getDevice().getRootDevice().getUSN();

          if (!new_usn.equals(existing_usn)) {

            multiple_found = true;

            break;
          }
        }

        if (multiple_found) {

          PluginConfig pc = plugin_interface.getPluginconfig();

          if (!pc.getPluginBooleanParameter("upnp.device.multipledevices.warned", false)) {

            pc.setPluginParameter("upnp.device.multipledevices.warned", true);

            String text = MessageText.getString("upnp.alert.multipledevice.warning");

            log.logAlertRepeatable(LoggerChannel.LT_WARNING, text);
          }
        }
      }

      checkState();

    } finally {

      this_mon.exit();
    }
  }