/* * 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.io; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.limewire.io.NetworkUtils; import org.limewire.mojito.KUID; import org.limewire.mojito.StatusCode; import org.limewire.mojito.db.DHTValue; import org.limewire.mojito.db.DHTValueEntity; import org.limewire.mojito.db.DHTValueFactoryManager; import org.limewire.mojito.db.DHTValueType; import org.limewire.mojito.messages.MessageID; import org.limewire.mojito.messages.DHTMessage.OpCode; import org.limewire.mojito.messages.StatsRequest.StatisticType; import org.limewire.mojito.messages.StoreResponse.StoreStatusCode; import org.limewire.mojito.messages.impl.DefaultMessageID; import org.limewire.mojito.routing.Contact; import org.limewire.mojito.routing.ContactFactory; import org.limewire.mojito.routing.Vendor; import org.limewire.mojito.routing.Version; import org.limewire.security.AddressSecurityToken; import org.limewire.security.MACCalculatorRepositoryManager; import org.limewire.security.SecurityToken; /** * The MessageInputStream reads (parses) a <code>DHTMessage</code> * from a given InputStream. * <p> * <strong>NOTE</strong>: This class is specific to Mojito's Gnutella backed * Message format. You may or may not be able to use parts of this * class for alternative message formats! */ public class MessageInputStream extends DataInputStream { private final MACCalculatorRepositoryManager MACCalculatorRepositoryManager; public MessageInputStream(InputStream in, MACCalculatorRepositoryManager MACCalculatorRepositoryManager) { super(in); this.MACCalculatorRepositoryManager = MACCalculatorRepositoryManager; } /** * Reads the given number of bytes. */ public byte[] readBytes(int bytes) throws IOException { byte[] buf = new byte[bytes]; readFully(buf); return buf; } /** * Reads a KUID from the InputStream. */ public KUID readKUID() throws IOException { return KUID.createWithInputStream(this); } /** * Reads a MessageID from the InputStream. */ public MessageID readMessageID() throws IOException { return DefaultMessageID.createWithInputStream(this, MACCalculatorRepositoryManager); } /** * Reads a BigInteger from the InputStream. */ public BigInteger readDHTSize() throws IOException { int length = readUnsignedByte(); if (length > KUID.LENGTH) { // can't be more than 2**160 bit throw new IOException("Illegal length: " + length); } byte[] num = readBytes(length); return new BigInteger(1 /* unsigned */, num); } /** * Reads a DHTValueEntity from the InputStream. * * @param sender the Contact that send us the DHTValue */ public DHTValueEntity readDHTValueEntity(Contact sender, DHTValueFactoryManager factoryManager) throws IOException { Contact creator = readContact(); KUID primaryKey = readKUID(); DHTValue value = readDHTValue(factoryManager); // if the creator has the same KUID as the sender, use the sender as we have its external addr. if (creator.getNodeID().equals(sender.getNodeID())) creator = sender; return DHTValueEntity.createFromRemote(creator, sender, primaryKey, value); } /** * Reads a DHTValue from the InputStream. */ private DHTValue readDHTValue(DHTValueFactoryManager factoryManager) throws IOException { DHTValueType valueType = readValueType(); Version version = readVersion(); byte[] data = null; int length = readUnsignedShort(); if (length > 0) { data = new byte[length]; readFully(data); } return factoryManager.createDHTValue(valueType, version, data); } /** * Reads multiple DHTValues from the InputStream. */ public List<DHTValueEntity> readDHTValueEntities(Contact sender, DHTValueFactoryManager factoryManager) throws IOException { int size = readUnsignedByte(); if (size == 0) { return Collections.emptyList(); } DHTValueEntity[] entities = new DHTValueEntity[size]; for(int i = 0; i < entities.length; i++) { entities[i] = readDHTValueEntity(sender, factoryManager); } return Arrays.asList(entities); } /** * Reads multiple KUIDs from the InputStream. */ public Collection<KUID> readKUIDs() throws IOException { int size = readUnsignedByte(); if (size == 0) { return Collections.emptySet(); } KUID[] keys = new KUID[size]; for (int i = 0; i < size; i++) { keys[i] = readKUID(); } return Arrays.asList(keys); } /** * Reads a Signature from the InputStream. */ public byte[] readSignature() throws IOException { int length = readUnsignedByte(); if (length == 0) { return null; } byte[] signature = new byte[length]; readFully(signature, 0, signature.length); return signature; } /** * Reads a Contact from the InputStream. */ public Contact readContact() throws IOException { Vendor vendor = readVendor(); Version version = readVersion(); KUID nodeId = readKUID(); SocketAddress addr = readSocketAddress(); if (addr == null) { throw new UnknownHostException("SocketAddress is "+addr); } return ContactFactory.createUnknownContact(vendor, version, nodeId, addr); } /** * Reads multiple Contacts from the InputStream. */ public Collection<Contact> readContacts() throws IOException { int size = readUnsignedByte(); if (size == 0) { return Collections.emptyList(); } Contact[] nodes = new Contact[size]; for(int i = 0; i < nodes.length; i++) { nodes[i] = readContact(); } return Arrays.asList(nodes); } /** * Reads an InetAddress from the InputStream. */ public InetAddress readInetAddress() throws IOException { int length = readUnsignedByte(); if (length == 0) { return null; } byte[] address = new byte[length]; readFully(address); return InetAddress.getByAddress(address); } /** * Reads a Port number from the InputStream. */ public int readPort() throws IOException { return readUnsignedShort(); } /** * Reads a SocketAddress from the InputStream. */ public InetSocketAddress readSocketAddress() throws IOException { InetAddress addr = readInetAddress(); if (addr == null || !NetworkUtils.isValidAddress(addr)) { return null; } int port = readPort(); return new InetSocketAddress(addr, port); } /** * Reads a AddressSecurityToken from the InputStream. */ public SecurityToken readSecurityToken() throws IOException { int length = readUnsignedByte(); if (length == 0) { return null; } byte[] securityToken = new byte[length]; readFully(securityToken, 0, securityToken.length); return new AddressSecurityToken(securityToken, MACCalculatorRepositoryManager); } /** * Reads an encoded Statistics. */ public byte[] readStatistics() throws IOException { int length = readUnsignedShort(); if (length == 0) { return null; } byte[] statistics = new byte[length]; readFully(statistics); return statistics; } /** * Reads an OpCode from the InputStream. */ public OpCode readOpCode() throws IOException { return OpCode.valueOf(readUnsignedByte()); } /** * Reads an Type from the InputStream. */ public StatisticType readStatisticType() throws IOException { return StatisticType.valueOf(readUnsignedByte()); } /** * Reads a StatusCode from the InputStream. */ public StatusCode readStatusCode() throws IOException { return StatusCode.valueOf(readUnsignedShort(), readDHTString()); } /** * Reads a String from the InputStream. See * MessageOutputStrea.writeDHTString(). * * @returns empty string if the length of the string is 0 */ private String readDHTString() throws IOException { int length = readUnsignedShort(); if (length == 0) { return ""; } byte[] b = new byte[length]; readFully(b); return new String(b, "UTF-8"); } /** * Reads and returns a Collection of StoreStatusCode(s). */ public Collection<StoreStatusCode> readStoreStatusCodes() throws IOException { int size = readUnsignedByte(); if (size == 0) { return Collections.emptySet(); } StoreStatusCode[] status = new StoreStatusCode[size]; for (int i = 0; i < size; i++) { KUID primaryKey = readKUID(); KUID secondaryKey = readKUID(); StatusCode statusCode = readStatusCode(); status[i] = new StoreStatusCode(primaryKey, secondaryKey, statusCode); } return Arrays.asList(status); } /** * Reads a DHTValueType from the InputStream. */ public DHTValueType readValueType() throws IOException { return DHTValueType.valueOf(readInt()); } /** * Reads a Vendor from the InputStream. */ public Vendor readVendor() throws IOException { return Vendor.valueOf(readInt()); } /** * Reads a Version from the InputStream. */ public Version readVersion() throws IOException { return Version.valueOf(readUnsignedShort()); } }