package net.i2p.util; import java.io.IOException; import java.io.Writer; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import net.i2p.I2PAppContext; /** * Map services to internal or external application ports * for this context. Not intended for the router's NTCP or SSU ports. * * @since 0.8.12 */ public class PortMapper { private final ConcurrentHashMap<String, InetSocketAddress> _dir; public static final String SVC_CONSOLE = "console"; public static final String SVC_HTTPS_CONSOLE = "https_console"; public static final String SVC_HTTP_PROXY = "HTTP"; public static final String SVC_HTTPS_PROXY = "HTTPS"; public static final String SVC_EEPSITE = "eepsite"; public static final String SVC_IRC = "irc"; public static final String SVC_SOCKS = "socks"; public static final String SVC_TAHOE = "tahoe-lafs"; public static final String SVC_SMTP = "SMTP"; public static final String SVC_POP = "POP3"; public static final String SVC_SAM = "SAM"; /** @since 0.9.24 */ public static final String SVC_SAM_UDP = "SAM-UDP"; /** @since 0.9.24 */ public static final String SVC_SAM_SSL = "SAM-SSL"; public static final String SVC_BOB = "BOB"; /** not necessary, already in config? */ public static final String SVC_I2CP = "I2CP"; /** @since 0.9.23 */ public static final String SVC_I2CP_SSL = "I2CP-SSL"; /** * @param context unused for now */ public PortMapper(I2PAppContext context) { _dir = new ConcurrentHashMap<String, InetSocketAddress>(8); } /** * Add the service * @param port > 0 * @return success, false if already registered */ public boolean register(String service, int port) { return register(service, "127.0.0.1", port); } /** * Add the service * @param port > 0 * @return success, false if already registered * @since 0.9.21 */ public boolean register(String service, String host, int port) { if (port <= 0 || port > 65535) return false; return _dir.putIfAbsent(service, InetSocketAddress.createUnresolved(host, port)) == null; } /** * Remove the service */ public void unregister(String service) { _dir.remove(service); } /** * Get the registered port for a service * @return -1 if not registered */ public int getPort(String service) { int port = getPort(service, -1); return port; } /** * Get the registered port for a service * @param def default * @return def if not registered */ public int getPort(String service, int def) { InetSocketAddress ia = _dir.get(service); if (ia == null) return def; return ia.getPort(); } /** * Get the registered host for a service. * Will return "127.0.0.1" if the service was registered without a host. * @param def default * @return def if not registered * @since 0.9.21 */ public String getHost(String service, String def) { InetSocketAddress ia = _dir.get(service); if (ia == null) return def; return ia.getHostName(); } /** * Get the actual host for a service. * Will return "127.0.0.1" if the service was registered without a host. * If the service was registered with the host "0.0.0.0", "::", or "0:0:0:0:0:0:0:0", * it will return a public IP if we have one, * else a local IP if we have one, else def. * If it was not registered with a wildcard address, it will return the registered host. * * @param def default * @return def if not registered * @since 0.9.24 */ public String getActualHost(String service, String def) { InetSocketAddress ia = _dir.get(service); if (ia == null) return def; return convertWildcard(ia.getHostName(), def); } /* * See above * @param def default * @return def if no ips * @since 0.9.24 */ private static String convertWildcard(String ip, String def) { String rv = ip; if (rv.equals("0.0.0.0")) { // public rv = Addresses.getAnyAddress(); if (rv == null) { rv = def; // local Set<String> addrs = Addresses.getAddresses(true, false); for (String addr : addrs) { if (!addr.startsWith("127.") && !addr.equals("0.0.0.0")) { rv = addr; break; } } } } else if (rv.equals("::") || rv.equals("0:0:0:0:0:0:0:0")) { rv = def; // public Set<String> addrs = Addresses.getAddresses(false, true); for (String addr : addrs) { if (!addr.contains(".")) { return rv; } } // local addrs = Addresses.getAddresses(true, true); for (String addr : addrs) { if (!addr.contains(".") && !addr.equals("::") && !addr.equals("0:0:0:0:0:0:0:0")) { rv = addr; break; } } } return rv; } /** * For debugging only * @since 0.9.20 */ public void renderStatusHTML(Writer out) throws IOException { List<String> services = new ArrayList<String>(_dir.keySet()); out.write("<h2>Port Mapper</h2><table><tr><th>Service<th>Host<th>Port\n"); Collections.sort(services); for (String s : services) { InetSocketAddress ia = _dir.get(s); if (ia == null) continue; out.write("<tr><td>" + s + "<td>" + convertWildcard(ia.getHostName(), "127.0.0.1") + "<td>" + ia.getPort() + '\n'); } out.write("</table>\n"); } }