/* * 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.internal; import com.sun.jini.discovery.ClientSubjectChecker; import com.sun.jini.discovery.DiscoveryProtocolException; import com.sun.jini.discovery.UnicastDiscoveryServer; import com.sun.jini.discovery.UnicastResponse; import com.sun.jini.jeri.internal.connection.ServerConnManager; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import javax.net.ServerSocketFactory; import javax.security.auth.Subject; import net.jini.core.constraint.InvocationConstraints; import net.jini.io.UnsupportedConstraintException; import net.jini.io.context.ClientSubject; import net.jini.jeri.InboundRequest; import net.jini.jeri.RequestDispatcher; import net.jini.jeri.ServerEndpoint; import net.jini.jeri.ServerEndpoint.ListenContext; import net.jini.jeri.ServerEndpoint.ListenCookie; import net.jini.jeri.ServerEndpoint.ListenEndpoint; import net.jini.jeri.ServerEndpoint.ListenHandle; import net.jini.jeri.connection.InboundRequestHandle; import net.jini.jeri.connection.ServerConnection; /** * Provides an abstract server endpoint-based UnicastDiscoveryServer * implementation, which serves as a superclass for server-side providers for * the net.jini.discovery.ssl and net.jini.discovery.kerberos unicast discovery * formats. */ public abstract class EndpointBasedServer extends EndpointBasedProvider implements UnicastDiscoveryServer { /** * Constructs instance with the given format name and object providing * access to non-public endpoint operations. */ protected EndpointBasedServer(String formatName, EndpointInternals endpointInternals) { super(formatName, endpointInternals); } // documentation inherited from UnicastDiscoveryServer public void checkUnicastDiscoveryConstraints( InvocationConstraints constraints) throws UnsupportedConstraintException { if (constraints == null) { constraints = InvocationConstraints.EMPTY; } ServerEndpoint ep = getServerEndpoint(null); checkIntegrity(ep.checkConstraints(constraints)); } // documentation inherited from UnicastDiscoveryServer public void handleUnicastDiscovery(UnicastResponse response, Socket socket, InvocationConstraints constraints, ClientSubjectChecker checker, Collection context, ByteBuffer received, ByteBuffer sent) throws IOException { if (response == null || socket == null || received == null || sent == null) { throw new NullPointerException(); } if (constraints == null) { constraints = InvocationConstraints.EMPTY; } ServerEndpoint ep = getServerEndpoint(new PrearrangedServerSocketFactory(socket)); ServerConnManagerImpl mgr = new ServerConnManagerImpl(); endpointInternals.setServerConnManager(ep, mgr); ListenContextImpl lc = new ListenContextImpl(); ep.enumerateListenEndpoints(lc); ServerConnection conn = mgr.getServerConnection(); try { InputStream in = new BufferedInputStream(conn.getInputStream()); OutputStream out = new BufferedOutputStream(conn.getOutputStream()); InboundRequestHandle handle = conn.processRequestData(in, out); conn.checkPermissions(handle); checkIntegrity(conn.checkConstraints(handle, constraints)); if (checker != null) { checker.checkClientSubject(getClientSubject(conn, handle)); } byte[] hash = calcHandshakeHash(received, sent); byte[] clientHash = new byte[hash.length]; new DataInputStream(in).readFully(clientHash); if (!Arrays.equals(clientHash, hash)) { throw new DiscoveryProtocolException( "handshake hash mismatch"); } Plaintext.writeUnicastResponse(out, response, context); out.flush(); } finally { conn.close(); lc.getListenHandle().close(); } } /** * Returns a server endpoint which uses the given server socket factory, if * non-null. Other parameters of the server endpoint, such as the listen * port, are irrelevant from the standpoint of this class and can be chosen * arbitrarily. */ protected abstract ServerEndpoint getServerEndpoint( ServerSocketFactory factory) throws UnsupportedConstraintException; /** * Returns the subject that the client of the given connection has * authenticated as, or null if the client is not authenticated. */ private static Subject getClientSubject(ServerConnection connection, InboundRequestHandle handle) { Collection ctx = new ArrayList(); connection.populateContext(handle, ctx); for (Iterator i = ctx.iterator(); i.hasNext(); ) { Object obj = i.next(); if (obj instanceof ClientSubject) { return ((ClientSubject) obj).getClientSubject(); } } return null; } /** * Server connection manager that stores the connection it is given to * handle. */ private static class ServerConnManagerImpl implements ServerConnManager { private ServerConnection conn = null; ServerConnManagerImpl() { } public synchronized void handleConnection(ServerConnection conn, RequestDispatcher disp) { if (conn == null || disp == null) { throw new NullPointerException(); } this.conn = conn; notifyAll(); } synchronized ServerConnection getServerConnection() { while (conn == null) { try { wait(); } catch (InterruptedException e) {} } return conn; } } /** * Listen context that listens on the endpoint it is given, and stores the * resulting handle. */ private static class ListenContextImpl implements ListenContext { private ListenHandle handle = null; public ListenCookie addListenEndpoint(ListenEndpoint endpoint) throws IOException { handle = endpoint.listen(new RequestDispatcher() { public void dispatch(InboundRequest req) { throw new AssertionError("dispatch should not occur"); } }); return handle.getCookie(); } ListenHandle getListenHandle() { return handle; } } /** * Server socket that returns a prearranged socket once from its accept * method, and then blocks on subsequent calls to accept until closed. */ private static class PrearrangedServerSocket extends ServerSocket { private Socket socket; private boolean closed = false; PrearrangedServerSocket(Socket socket) throws IOException { this.socket = socket; } public synchronized Socket accept() throws IOException { if (!closed && socket != null) { Socket s = socket; socket = null; return s; } while (!closed) { try { wait(); } catch (InterruptedException e) {} } throw new SocketException("socket closed"); } public int getLocalPort() { /* * Although this port number is bogus, it has to be plausible, * otherwise calling enumerateListenEndpoints on the server * endpoint will fail since an endpoint cannot be created. */ return 1; } public synchronized void close() throws IOException { if (!closed) { closed = true; notifyAll(); } } } /** * Server socket factory that returns a PrearrangedServerSocket for a * prearranged socket. */ private static class PrearrangedServerSocketFactory extends ServerSocketFactory { private final ServerSocket ssocket; PrearrangedServerSocketFactory(Socket socket) throws IOException { ssocket = new PrearrangedServerSocket(socket); } public ServerSocket createServerSocket() throws IOException { return ssocket; } public ServerSocket createServerSocket(int port) throws IOException { return ssocket; } public ServerSocket createServerSocket(int port, int backlog) throws IOException { return ssocket; } public ServerSocket createServerSocket(int port, int backlog, InetAddress addr) throws IOException { return ssocket; } } }