/* * Copyright 2011 Google Inc. * Copyright 2014 Andreas Schildbach * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.bitcoinj.core; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * <p>Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is * one of the ways peers can find each other without using the DNS or IRC discovery mechanisms. However storing and * using addr messages is not presently implemented.</p> * * <p>Instances of this class are not safe for use by multiple threads.</p> */ public class AddressMessage extends Message { private static final long MAX_ADDRESSES = 1024; private List<PeerAddress> addresses; /** * Contruct a new 'addr' message. * @param params NetworkParameters object. * @param offset The location of the first payload byte within the array. * @param parseRetain Whether to retain the backing byte array for quick reserialization. * If true and the backing byte array is invalidated due to modification of a field then * the cached bytes may be repopulated and retained if the message is serialized again in the future. * @param length The length of message if known. Usually this is provided when deserializing of the wire * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH * @throws ProtocolException */ AddressMessage(NetworkParameters params, byte[] payload, int offset, MessageSerializer setSerializer, int length) throws ProtocolException { super(params, payload, offset, setSerializer, length); } /** * Contruct a new 'addr' message. * @param params NetworkParameters object. * @param serializer the serializer to use for this block. * @param length The length of message if known. Usually this is provided when deserializing of the wire * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH * @throws ProtocolException */ AddressMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException { super(params, payload, 0, serializer, length); } AddressMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException { super(params, payload, offset, params.getDefaultSerializer(), UNKNOWN_LENGTH); } AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException { super(params, payload, 0, params.getDefaultSerializer(), UNKNOWN_LENGTH); } @Override protected void parse() throws ProtocolException { long numAddresses = readVarInt(); // Guard against ultra large messages that will crash us. if (numAddresses > MAX_ADDRESSES) throw new ProtocolException("Address message too large."); addresses = new ArrayList<PeerAddress>((int) numAddresses); for (int i = 0; i < numAddresses; i++) { PeerAddress addr = new PeerAddress(params, payload, cursor, protocolVersion, this, serializer); addresses.add(addr); cursor += addr.getMessageSize(); } length = new VarInt(addresses.size()).getSizeInBytes(); // The 4 byte difference is the uint32 timestamp that was introduced in version 31402 length += addresses.size() * (protocolVersion > 31402 ? PeerAddress.MESSAGE_SIZE : PeerAddress.MESSAGE_SIZE - 4); } @Override protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { if (addresses == null) return; stream.write(new VarInt(addresses.size()).encode()); for (PeerAddress addr : addresses) { addr.bitcoinSerialize(stream); } } /** * @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be safely modified. */ public List<PeerAddress> getAddresses() { return Collections.unmodifiableList(addresses); } public void addAddress(PeerAddress address) { unCache(); address.setParent(this); addresses.add(address); if (length == UNKNOWN_LENGTH) getMessageSize(); else length += address.getMessageSize(); } public void removeAddress(int index) { unCache(); PeerAddress address = addresses.remove(index); address.setParent(null); if (length == UNKNOWN_LENGTH) getMessageSize(); else length -= address.getMessageSize(); } @Override public String toString() { return "addr: " + Utils.join(addresses); } }