/* * Mojito Distributed Hash Table (Mojito DHT) * Copyright (C) 2006-2007 LimeWire LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.limewire.mojito.routing.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.io.NetworkUtils; import org.limewire.mojito.KUID; import org.limewire.mojito.routing.Contact; import org.limewire.mojito.routing.Vendor; import org.limewire.mojito.routing.Version; import org.limewire.mojito.util.ContactUtils; /** * Stores the contact information of the local Node. * Some methods of <code>LocalContact</code> do nothing or return hard coded * values like getting and setting the Round Trip Time (RTT). * <code>LocalContact</code> provides some additional Methods to update the * local Node's contact information. */ public class LocalContact implements Contact { private static final Log LOG = LogFactory.getLog(LocalContact.class); private static final long serialVersionUID = -1372388406248015059L; private volatile Vendor vendor; private volatile Version version; private volatile KUID nodeId; private volatile int instanceId; private volatile int flags; private transient volatile SocketAddress sourceAddress; private transient volatile SocketAddress contactAddress; private transient SocketAddress tmpExternalAddress; public LocalContact(Vendor vendor, Version version, KUID nodeId, int instanceId, boolean firewalled) { this(vendor, version, nodeId, instanceId, (firewalled ? FIREWALLED_FLAG : DEFAULT_FLAG)); } public LocalContact(Vendor vendor, Version version, KUID nodeId, int instanceId, int flags) { this.vendor = vendor; this.version = version; this.nodeId = nodeId; this.instanceId = instanceId; this.flags = flags; init(); } private void init() { contactAddress = new InetSocketAddress("localhost", 0); } /** * Sets the local Node's vendor code. */ public void setVendor(Vendor vendor) { this.vendor = vendor; } public Vendor getVendor() { return vendor; } /** * Sets the local Node's version number. */ public void setVersion(Version version) { this.version = version; } public Version getVersion() { return version; } /** * Sets the local Node's KUID. * <p> * NOTE: This requires a rebuild of the RouteTable! */ public void setNodeID(KUID nodeId) { this.nodeId = nodeId; } public KUID getNodeID() { return nodeId; } public int getInstanceID() { return instanceId; } public int getFlags() { return flags; } /** * Sets the instanceId to the next value. */ public void nextInstanceID() { instanceId = (instanceId + 1) % 0xFF; } public SocketAddress getContactAddress() { return contactAddress; } /** * Sets the local Node's Contact (external) Address. */ public synchronized void setContactAddress(SocketAddress contactAddress) { this.contactAddress = contactAddress; this.tmpExternalAddress = null; } /** * Sets the local Node's Contact (external) Port. */ public synchronized void setExternalPort(int port) { InetSocketAddress addr = (InetSocketAddress)getContactAddress(); setContactAddress(new InetSocketAddress(addr.getAddress(), port)); } /** * Returns the local Node's external Port. */ public int getExternalPort() { return ((InetSocketAddress)getContactAddress()).getPort(); } public SocketAddress getSourceAddress() { return sourceAddress; } public synchronized void setSourceAddress(SocketAddress sourceAddress) { this.sourceAddress = sourceAddress; } public boolean isFirewalled() { return (flags & FIREWALLED_FLAG) != 0; } /** * Sets whether or not this Contact is firewalled. */ public void setFirewalled(boolean firewalled) { if (isFirewalled() != firewalled) { this.flags ^= FIREWALLED_FLAG; } } /** * Sets the external Address of the local Node. */ public synchronized boolean setExternalAddress(SocketAddress externalSocketAddress) { if (externalSocketAddress == null) { if (LOG.isDebugEnabled()) { LOG.debug("SocketAddress is null"); } return false; } // --- DOES NOT CHANGE THE PORT! --- InetAddress externalAddress = ((InetSocketAddress)externalSocketAddress).getAddress(); //int externalPort = ((InetSocketAddress)externalSocketAddress).getPort(); InetAddress currentAddress = ((InetSocketAddress)getContactAddress()).getAddress(); int currentPort = ((InetSocketAddress)getContactAddress()).getPort(); if (externalAddress.equals(currentAddress)) { if (LOG.isInfoEnabled()) { LOG.info("Reported external address is equal to current external address: " + externalAddress); } return false; } // There's no reason to set the external address to a // PRIVATE IP address. This can happen with a Node that // is pinging an another Node that is behind the same // NAT router if (ContactUtils.isPrivateAddress(externalAddress)) { if (LOG.isInfoEnabled()) { LOG.info(externalAddress + " is a PRIVATE address"); } return false; } if (!NetworkUtils.isSameAddressSpace( externalAddress, currentAddress)) { // The remote Node tries to set our external address // to an address that's from a different address space? if (LOG.isWarnEnabled()) { LOG.warn("The current external address " + currentAddress + " is from a different IP address space than " + externalAddress); } return false; } SocketAddress addr = new InetSocketAddress(externalAddress, currentPort); if (tmpExternalAddress == null || tmpExternalAddress.equals(addr)) { if (LOG.isInfoEnabled()) { LOG.info("Setting the current external address from " + contactAddress + " to " + addr); } contactAddress = addr; //if (externalPort == currentPort) {} } tmpExternalAddress = addr; return true; } /** * Hard coded to return 0. */ public int getFailures() { return 0; } /** * Hard coded to return 0L. */ public long getLastFailedTime() { return 0L; } /** * Does nothing. */ public void setRoundTripTime(long rtt) { } /** * Hard coded to return 0L. */ public long getRoundTripTime() { return 0L; } /** * Does nothing. */ public void setTimeStamp(long timeStamp) { } /** * Hard coded to return @see #LOCAL_CONTACT. */ public long getTimeStamp() { return LOCAL_CONTACT; } /** * Hard coded to return 0L. */ public long getAdaptativeTimeout() { return 0L; } /** * Does nothing. */ public void handleFailure() { } /** * Hard coded to return true. */ public boolean hasBeenRecentlyAlive() { return true; } /** * Hard coded to return false. */ public boolean hasFailed() { return false; } /** * Hard coded to return false. This might sound * strange since the local Node is always alive * but the idea in some cases is to contact alive * Nodes only and as there's no reason to contact * the local Node. */ public boolean isAlive() { return false; } /** * Hard coded to return false. */ public boolean isDead() { return false; } /** * Hard coded to return true. * * @see #isAlive() */ public boolean isUnknown() { return true; } /** * Does nothing. */ public void unknown() { } public boolean isShutdown() { return (flags & SHUTDOWN_FLAG) != 0; } public void shutdown(boolean shutdown) { if (isShutdown() != shutdown) { this.flags ^= SHUTDOWN_FLAG; } } /** * Does nothing if 'existing' is the same instance * as 'this'. Throws an UnsupportedOperationException * if 'existing' is a different instance. * <p> * The UnsupportedOperationException exception is mainly * thrown for debugging purposes and should never ever * happen. If it does then there's a bug in the RouteTable * update logic! */ public void updateWithExistingContact(Contact existing) { if (existing != this) { throw new UnsupportedOperationException(); } } @Override public int hashCode() { return nodeId.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof LocalContact)) { return false; } return nodeId.equals(((Contact)o).getNodeID()); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); init(); } @Override public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append(ContactUtils.toString(getNodeID(), getContactAddress())) .append(", instanceId=").append(getInstanceID()) .append(", firewalled=").append(isFirewalled()); return buffer.toString(); } }