/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.sun.jini.discovery; import com.sun.jini.discovery.internal.Plaintext; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UTFDataFormatException; import java.net.DatagramPacket; import java.net.Socket; import java.nio.ByteBuffer; import java.rmi.MarshalledObject; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.jini.core.constraint.InvocationConstraints; import net.jini.core.lookup.ServiceID; import net.jini.core.lookup.ServiceRegistrar; import net.jini.discovery.Constants; import net.jini.io.MarshalledInstance; import net.jini.io.UnsupportedConstraintException; /** * Class providing methods for implementing discovery protocol version 1. */ class DiscoveryV1 extends Discovery { private static final int SERVICE_ID_LEN = 16; private static final DiscoveryV1 instance = new DiscoveryV1(); private static final Logger logger = Logger.getLogger(DiscoveryV1.class.getName()); static DiscoveryV1 getInstance() { return instance; } public EncodeIterator encodeMulticastRequest( final MulticastRequest request, final int maxPacketSize, final InvocationConstraints constraints) { if (maxPacketSize < MIN_MAX_PACKET_SIZE) { throw new IllegalArgumentException("maxPacketSize too small"); } return new EncodeIterator() { private boolean used; public DatagramPacket[] next() throws IOException { used = true; return encode(request, maxPacketSize, constraints); } public boolean hasNext() { return !used; } }; } private static DatagramPacket[] encode(MulticastRequest request, int maxPacketSize, InvocationConstraints constraints) throws IOException { final int MIN_DATA_LEN = 16; final int NUM_GROUPS_LEN = 4; final int NUM_SERVICE_IDS_LEN = 4; checkConstraints(constraints); // precompute length of UTF-encoded group names LinkedList groups = new LinkedList(); String[] g = request.getGroups(); for (int i = 0; i < g.length; i++) { byte[] b = Plaintext.toUtf(g[i]); if (b.length + MIN_DATA_LEN > maxPacketSize) { throw new DiscoveryProtocolException( "group too long: " + g[i]); } groups.add(b); } List packets = new ArrayList(); do { ByteBuffer buf = ByteBuffer.allocate(maxPacketSize); // write protocol version buf.putInt(PROTOCOL_VERSION_1); // write client port buf.putInt(request.getPort()); // write known service IDs ServiceID[] ids = request.getServiceIDs(); int nids = Math.min( ids.length, (buf.remaining() - (groups.isEmpty() ? 0 : ((byte[]) groups.getFirst()).length) - NUM_SERVICE_IDS_LEN - NUM_GROUPS_LEN) / SERVICE_ID_LEN); buf.putInt(nids); for (int i = 0; i < nids; i++) { buf.putLong(ids[i].getMostSignificantBits()); buf.putLong(ids[i].getLeastSignificantBits()); } // write lookup groups int ngroupsPos = buf.position(); int ngroups = 0; buf.putInt(ngroups); while (!groups.isEmpty()) { if (((byte[]) groups.getFirst()).length > buf.remaining()) { break; } buf.put((byte[]) groups.removeFirst()); ngroups++; } if (ngroups > 0) { buf.putInt(ngroupsPos, ngroups); } packets.add(new DatagramPacket(buf.array(), buf.position(), Constants.getRequestAddress(), Constants.discoveryPort)); } while (!groups.isEmpty()); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "encoded {0}", new Object[]{ request }); } return (DatagramPacket[]) packets.toArray(new DatagramPacket[packets.size()]); } public MulticastRequest decodeMulticastRequest( DatagramPacket packet, InvocationConstraints constraints, ClientSubjectChecker checker) throws IOException { checkConstraints(constraints); if (checker != null) { checker.checkClientSubject(null); } try { ByteBuffer buf = ByteBuffer.wrap(packet.getData(), packet.getOffset(), packet.getLength()); // read protocol version int version = buf.getInt(); if (version != PROTOCOL_VERSION_1) { throw new DiscoveryProtocolException( "wrong protocol version: " + version); } // derive client host String host = packet.getAddress().getHostAddress(); // read client port int port = buf.getInt(); // read known service IDs int nids = buf.getInt(); if (nids < 0 || nids > buf.remaining() / SERVICE_ID_LEN) { throw new DiscoveryProtocolException( "invalid service ID count: " + nids); } ServiceID[] ids = new ServiceID[nids]; for (int i = 0; i < ids.length; i++) { long hi = buf.getLong(); long lo = buf.getLong(); ids[i] = new ServiceID(hi, lo); } // read lookup groups int ngroups = buf.getInt(); if (ngroups < 0 || ngroups > buf.remaining() / 2) { throw new DiscoveryProtocolException( "invalid group count: " + ngroups); } String[] groups = new String[ngroups]; for (int i = 0; i < groups.length; i++) { groups[i] = Plaintext.getUtf(buf); } MulticastRequest req = new MulticastRequest(host, port, groups, ids); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "decoded {0}", new Object[]{ req }); } return req; } catch (RuntimeException e) { throw new DiscoveryProtocolException(null, e); } } public EncodeIterator encodeMulticastAnnouncement( final MulticastAnnouncement announcement, final int maxPacketSize, final InvocationConstraints constraints) { if (maxPacketSize < MIN_MAX_PACKET_SIZE) { throw new IllegalArgumentException("maxPacketSize too small"); } return new EncodeIterator() { private boolean used; public DatagramPacket[] next() throws IOException { used = true; return encode(announcement, maxPacketSize, constraints); } public boolean hasNext() { return !used; } }; } private static DatagramPacket[] encode(MulticastAnnouncement announcement, int maxPacketSize, InvocationConstraints constraints) throws IOException { final int MIN_DATA_LEN = 28; checkConstraints(constraints); // precompute length of UTF-encoded group names LinkedList groups = new LinkedList(); byte[] host = Plaintext.toUtf(announcement.getHost()); String[] g = announcement.getGroups(); for (int i = 0; i < g.length; i++) { byte[] b = Plaintext.toUtf(g[i]); if (b.length + host.length + MIN_DATA_LEN > maxPacketSize) { throw new DiscoveryProtocolException( "group too long: " + g[i]); } groups.add(b); } List packets = new ArrayList(); do { ByteBuffer buf = ByteBuffer.allocate(maxPacketSize); // write protocol version buf.putInt(PROTOCOL_VERSION_1); // write LUS host buf.put(host); // write LUS port buf.putInt(announcement.getPort()); // write LUS service ID ServiceID id = announcement.getServiceID(); buf.putLong(id.getMostSignificantBits()); buf.putLong(id.getLeastSignificantBits()); // write LUS member groups int ngroupsPos = buf.position(); int ngroups = 0; buf.putInt(ngroups); while (!groups.isEmpty()) { if (((byte[]) groups.getFirst()).length > buf.remaining()) { break; } buf.put((byte[]) groups.removeFirst()); ngroups++; } if (ngroups > 0) { buf.putInt(ngroupsPos, ngroups); } packets.add(new DatagramPacket(buf.array(), buf.position(), Constants.getAnnouncementAddress(), Constants.discoveryPort)); } while (!groups.isEmpty()); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "encoded {0}", new Object[]{ announcement }); } return (DatagramPacket[]) packets.toArray(new DatagramPacket[packets.size()]); } public MulticastAnnouncement decodeMulticastAnnouncement( DatagramPacket packet, InvocationConstraints constraints) throws IOException { checkConstraints(constraints); try { ByteBuffer buf = ByteBuffer.wrap(packet.getData(), packet.getOffset(), packet.getLength()); // read protocol version int version = buf.getInt(); if (version != PROTOCOL_VERSION_1) { throw new DiscoveryProtocolException( "wrong protocol version: " + version); } // read LUS host String host = Plaintext.getUtf(buf); // read LUS port int port = buf.getInt(); // read LUS service ID long hi = buf.getLong(); long lo = buf.getLong(); ServiceID id = new ServiceID(hi, lo); // read LUS member groups int ngroups = buf.getInt(); if (ngroups < 0 || ngroups > buf.remaining() / 2) { throw new DiscoveryProtocolException( "invalid group count: " + ngroups); } String[] groups = new String[ngroups]; for (int i = 0; i < groups.length; i++) { groups[i] = Plaintext.getUtf(buf); } MulticastAnnouncement ann = new MulticastAnnouncement(-1, host, port, groups, id); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "decoded {0}", new Object[]{ ann }); } return ann; } catch (RuntimeException e) { throw new DiscoveryProtocolException(null, e); } } public UnicastResponse doUnicastDiscovery( Socket socket, InvocationConstraints constraints, ClassLoader defaultLoader, ClassLoader verifierLoader, Collection context) throws IOException, ClassNotFoundException { checkConstraints(constraints); try { DataOutputStream dout = new DataOutputStream( new BufferedOutputStream(socket.getOutputStream(), 4)); // write unicast request dout.writeInt(PROTOCOL_VERSION_1); dout.flush(); // derive LUS host, port String host = socket.getInetAddress().getHostAddress(); int port = socket.getPort(); // read LUS proxy ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream(socket.getInputStream())); MarshalledInstance mi = new MarshalledInstance((MarshalledObject) oin.readObject()); ServiceRegistrar reg = (ServiceRegistrar) mi.get(defaultLoader, false, null, context); // read LUS member groups int ngroups = oin.readInt(); if (ngroups < 0) { throw new DiscoveryProtocolException( "invalid group count: " + ngroups); } String[] groups = new String[ngroups]; for (int i = 0; i < groups.length; i++) { groups[i] = oin.readUTF(); } UnicastResponse resp = new UnicastResponse(host, port, groups, reg); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "received {0}", new Object[]{ resp }); } return resp; } catch (RuntimeException e) { throw new DiscoveryProtocolException(null, e); } } public void handleUnicastDiscovery(UnicastResponse response, Socket socket, InvocationConstraints constraints, ClientSubjectChecker checker, Collection context) throws IOException { checkConstraints(constraints); if (checker != null) { checker.checkClientSubject(null); } // note: unicast request (the protocol version) already consumed // write LUS proxy ObjectOutputStream oout = new ObjectOutputStream( new BufferedOutputStream(socket.getOutputStream())); oout.writeObject( new MarshalledInstance( response.getRegistrar(), context).convertToMarshalledObject()); // write LUS member groups String[] groups = response.getGroups(); oout.writeInt(groups.length); for (int i = 0; i < groups.length; i++) { oout.writeUTF(groups[i]); } oout.flush(); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "sent {0}", new Object[]{ response }); } } public String toString() { return "DiscoveryV1"; } private static void checkConstraints(InvocationConstraints constraints) throws UnsupportedConstraintException { if (constraints != null) { constraints = constraints.makeAbsolute(); } Plaintext.checkConstraints(constraints); } }