/* * 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.messages.impl; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Arrays; import java.util.Random; import org.limewire.mojito.messages.MessageID; import org.limewire.mojito.util.ArrayUtils; import org.limewire.security.AbstractSecurityToken; import org.limewire.security.AddressSecurityToken; import org.limewire.security.InvalidSecurityTokenException; import org.limewire.security.MACCalculatorRepositoryManager; import org.limewire.security.SecurityToken; /** * An 128bit, pseudo random implementation of MessageID with support * for tagging. * <p> * This class is immutable! */ public class DefaultMessageID implements MessageID, Comparable<DefaultMessageID> { private static final long serialVersionUID = -1477232241287654597L; public static final int LENGTH = 16; private static final Random GENERATOR = new Random(); /** * A random pad we're using to obfuscate the actual * AddressSecurityToken. Nodes must do a lookup to get the QK! */ private static final byte[] RANDOM_PAD = new byte[4]; static { GENERATOR.nextBytes(RANDOM_PAD); } private final byte[] messageId; private final int hashCode; private final MACCalculatorRepositoryManager macManager; private DefaultMessageID(byte[] messageId, MACCalculatorRepositoryManager macManager) { if (messageId == null) { throw new NullPointerException("messageId cannot be null"); } if (messageId.length != LENGTH) { throw new IllegalArgumentException("MessageID must be " + LENGTH + " bytes long: " + messageId.length); } this.macManager = macManager; this.messageId = messageId; this.hashCode = Arrays.hashCode(messageId); } /** * Creates a MessageID from the given InputStream. */ public static DefaultMessageID createWithInputStream(InputStream in, MACCalculatorRepositoryManager manager) throws IOException { byte[] messageId = new byte[LENGTH]; int len = -1; int r = 0; while(r < messageId.length) { len = in.read(messageId, r, messageId.length-r); if (len < 0) { throw new EOFException(); } r += len; } return new DefaultMessageID(messageId, manager); } /** * Creates and returns a MessageID from the given byte Array. */ public static DefaultMessageID createWithBytes(byte[] messageId) { byte[] copy = new byte[messageId.length]; System.arraycopy(messageId, 0, copy, 0, messageId.length); return new DefaultMessageID(copy, null); } /** * Creates a pseudo random MessageID and tags it with the given * SocketAddress. */ public static DefaultMessageID createWithSocketAddress(SocketAddress dst, MACCalculatorRepositoryManager macManager) { byte[] messageId = new byte[LENGTH]; GENERATOR.nextBytes(messageId); if (dst instanceof InetSocketAddress) { byte[] token = (new MessageSecurityToken(new DHTTokenData(dst), macManager)).getBytes(); System.arraycopy(token, 0, messageId, 0, 4); } return new DefaultMessageID(messageId, macManager); } /** * Creates and returns a MessageID from the given * hex encoded String. */ public static DefaultMessageID createWithHexString(String messageId) { return new DefaultMessageID(ArrayUtils.parseHexString(messageId), null); } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#write(java.io.OutputStream) */ public void write(OutputStream os) throws IOException { os.write(messageId, 0, messageId.length); } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#getLength() */ public int getLength() { return LENGTH; } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#getBytes() */ public byte[] getBytes() { return getBytes(0, new byte[messageId.length], 0, messageId.length); } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#getBytes(int, byte[], int, int) */ public byte[] getBytes(int srcPos, byte[] dest, int destPos, int length) { System.arraycopy(messageId, srcPos, dest, destPos, length); return dest; } /** * Extracts and returns the AddressSecurityToken from the GUID. * @throws InvalidSecurityTokenException */ private SecurityToken getSecurityToken() throws InvalidSecurityTokenException { byte[] token = new byte[4]; System.arraycopy(messageId, 0, token, 0, token.length); return new MessageSecurityToken(token, macManager); } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#isTaggingSupported() */ public boolean isTaggingSupported() { return true; } /* * (non-Javadoc) * @see org.limewire.mojito.messages.MessageID#verifySecurityToken(java.net.SocketAddress) */ public boolean isFor(SocketAddress src) { if (!(src instanceof InetSocketAddress)) { return false; } try { SecurityToken token = getSecurityToken(); DHTTokenData data = new DHTTokenData(src); return token.isFor(data); } catch (InvalidSecurityTokenException iste) { return false; } } public int compareTo(DefaultMessageID o) { int d = 0; for(int i = 0; i < messageId.length; i++) { d = (messageId[i] & 0xFF) - (o.messageId[i] & 0xFF); if (d < 0) { return -1; } else if (d > 0) { return 1; } } return 0; } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof DefaultMessageID)) { return false; } return Arrays.equals(messageId, ((DefaultMessageID)o).messageId); } /** * Returns this MessageID as a hex String. */ public String toHexString() { return ArrayUtils.toHexString(messageId); } /** * Returns this MessageID as a binary String. */ public String toBinString() { return ArrayUtils.toBinString(messageId); } @Override public String toString() { return "MessageID: " + toHexString(); } public static class DHTTokenData extends AddressSecurityToken.AddressTokenData { private final SocketAddress addr; public DHTTokenData(SocketAddress addr) { super(addr); this.addr = addr; for (int i = 0; i < RANDOM_PAD.length; i++) data[i] ^= RANDOM_PAD[i]; } @Override public String toString() { return "DHTTokenData: " + ArrayUtils.toHexString(getData())+ " for "+addr; } } public static class MessageSecurityToken extends AbstractSecurityToken { public MessageSecurityToken(byte[] network, MACCalculatorRepositoryManager manager) throws InvalidSecurityTokenException { super(network, manager); } public MessageSecurityToken(DHTTokenData data, MACCalculatorRepositoryManager manager) { super(data, manager); } @Override protected byte[] getFromMAC(byte[] mac, TokenData ignored) { return mac; } @Override public String toString() { return "MessageSecurityToken: " + ArrayUtils.toHexString(getBytes()); } } }