1 /*
   2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.rmi.server;
  27 
  28 import java.io.IOException;
  29 import java.io.ObjectInput;
  30 import java.io.ObjectInputFilter;
  31 import java.io.ObjectInputStream;
  32 import java.io.ObjectOutput;
  33 import java.lang.reflect.InvocationTargetException;
  34 import java.lang.reflect.Method;
  35 import java.rmi.AccessException;
  36 import java.rmi.MarshalException;
  37 import java.rmi.Remote;
  38 import java.rmi.RemoteException;
  39 import java.rmi.ServerError;
  40 import java.rmi.ServerException;
  41 import java.rmi.UnmarshalException;
  42 import java.rmi.server.ExportException;
  43 import java.rmi.server.RemoteCall;
  44 import java.rmi.server.RemoteRef;
  45 import java.rmi.server.RemoteStub;
  46 import java.rmi.server.ServerNotActiveException;
  47 import java.rmi.server.ServerRef;
  48 import java.rmi.server.Skeleton;
  49 import java.rmi.server.SkeletonNotFoundException;
  50 import java.security.AccessController;
  51 import java.security.PrivilegedAction;
  52 import java.util.Collections;
  53 import java.util.Date;
  54 import java.util.HashMap;
  55 import java.util.Map;
  56 import java.util.WeakHashMap;
  57 import java.util.concurrent.atomic.AtomicInteger;
  58 import sun.rmi.runtime.Log;
  59 import sun.rmi.transport.LiveRef;
  60 import sun.rmi.transport.StreamRemoteCall;
  61 import sun.rmi.transport.Target;
  62 import sun.rmi.transport.tcp.TCPTransport;
  63 
  64 /**
  65  * UnicastServerRef implements the remote reference layer server-side
  66  * behavior for remote objects exported with the "UnicastRef" reference
  67  * type.
  68  * If an {@link ObjectInputFilter ObjectInputFilter} is supplied it is
  69  * invoked during deserialization to filter the arguments,
  70  * otherwise the default filter of {@link ObjectInputStream ObjectInputStream}
  71  * applies.
  72  *
  73  * @author  Ann Wollrath
  74  * @author  Roger Riggs
  75  * @author  Peter Jones
  76  */
  77 @SuppressWarnings("deprecation")
  78 public class UnicastServerRef extends UnicastRef
  79     implements ServerRef, Dispatcher
  80 {
  81     /** value of server call log property */
  82     public static final boolean logCalls = AccessController.doPrivileged(
  83         (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("java.rmi.server.logCalls"));
  84 
  85     /** server call log */
  86     public static final Log callLog =
  87         Log.getLog("sun.rmi.server.call", "RMI", logCalls);
  88 
  89     // use serialVersionUID from JDK 1.2.2 for interoperability
  90     private static final long serialVersionUID = -7384275867073752268L;
  91 
  92     /** flag to enable writing exceptions to System.err */
  93     private static final boolean wantExceptionLog =
  94         AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
  95             Boolean.getBoolean("sun.rmi.server.exceptionTrace"));
  96 
  97     private boolean forceStubUse = false;
  98 
  99     /**
 100      * flag to remove server-side stack traces before marshalling
 101      * exceptions thrown by remote invocations to this VM
 102      */
 103     private static final boolean suppressStackTraces =
 104         AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
 105             Boolean.getBoolean("sun.rmi.server.suppressStackTraces"));
 106 
 107     /**
 108      * skeleton to dispatch remote calls through, for 1.1 stub protocol
 109      * (may be null if stub class only uses 1.2 stub protocol)
 110      */
 111     private transient Skeleton skel;
 112 
 113     // The ObjectInputFilter for checking the invocation arguments
 114     private final transient ObjectInputFilter filter;
 115 
 116     /** maps method hash to Method object for each remote method */
 117     private transient Map<Long,Method> hashToMethod_Map = null;
 118 
 119     /**
 120      * A weak hash map, mapping classes to hash maps that map method
 121      * hashes to method objects.
 122      **/
 123     private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps =
 124         new HashToMethod_Maps();
 125 
 126     /** cache of impl classes that have no corresponding skeleton class */
 127     private static final Map<Class<?>,?> withoutSkeletons =
 128         Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>());
 129 
 130     private final AtomicInteger methodCallIDCount = new AtomicInteger(0);
 131 
 132     /**
 133      * Create a new (empty) Unicast server remote reference.
 134      * The filter is null to defer to the  default ObjectInputStream filter, if any.
 135      */
 136     public UnicastServerRef() {
 137         this.filter = null;
 138     }
 139 
 140     /**
 141      * Construct a Unicast server remote reference for a specified
 142      * liveRef.
 143      * The filter is null to defer to the  default ObjectInputStream filter, if any.
 144      */
 145     public UnicastServerRef(LiveRef ref) {
 146         super(ref);
 147         this.filter = null;
 148     }
 149 
 150     /**
 151      * Construct a Unicast server remote reference for a specified
 152      * liveRef and filter.
 153      */
 154     public UnicastServerRef(LiveRef ref, ObjectInputFilter filter) {
 155         super(ref);
 156         this.filter = filter;
 157     }
 158 
 159     /**
 160      * Construct a Unicast server remote reference to be exported
 161      * on the specified port.
 162      */
 163     public UnicastServerRef(int port) {
 164         super(new LiveRef(port));
 165         this.filter = null;
 166     }
 167 
 168     /**
 169      * Constructs a UnicastServerRef to be exported on an
 170      * anonymous port (i.e., 0) and that uses a pregenerated stub class
 171      * (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'.
 172      *
 173      * This constructor is only called by the method
 174      * UnicastRemoteObject.exportObject(Remote) passing 'true' for
 175      * 'forceStubUse'.  The UnicastRemoteObject.exportObject(Remote) method
 176      * returns RemoteStub, so it must ensure that the stub for the
 177      * exported object is an instance of a pregenerated stub class that
 178      * extends RemoteStub (instead of an instance of a dynamic proxy class
 179      * which is not an instance of RemoteStub).
 180      **/
 181     public UnicastServerRef(boolean forceStubUse) {
 182         this(0);
 183         this.forceStubUse = forceStubUse;
 184     }
 185 
 186     /**
 187      * With the addition of support for dynamic proxies as stubs, this
 188      * method is obsolete because it returns RemoteStub instead of the more
 189      * general Remote.  It should not be called.  It sets the
 190      * 'forceStubUse' flag to true so that the stub for the exported object
 191      * is forced to be an instance of the pregenerated stub class, which
 192      * extends RemoteStub.
 193      *
 194      * Export this object, create the skeleton and stubs for this
 195      * dispatcher.  Create a stub based on the type of the impl,
 196      * initialize it with the appropriate remote reference. Create the
 197      * target defined by the impl, dispatcher (this) and stub.
 198      * Export that target via the Ref.
 199      **/
 200     public RemoteStub exportObject(Remote impl, Object data)
 201         throws RemoteException
 202     {
 203         forceStubUse = true;
 204         return (RemoteStub) exportObject(impl, data, false);
 205     }
 206 
 207     /**
 208      * Export this object, create the skeleton and stubs for this
 209      * dispatcher.  Create a stub based on the type of the impl,
 210      * initialize it with the appropriate remote reference. Create the
 211      * target defined by the impl, dispatcher (this) and stub.
 212      * Export that target via the Ref.
 213      */
 214     public Remote exportObject(Remote impl, Object data,
 215                                boolean permanent)
 216         throws RemoteException
 217     {
 218         Class<?> implClass = impl.getClass();
 219         Remote stub;
 220 
 221         try {
 222             stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
 223         } catch (IllegalArgumentException e) {
 224             throw new ExportException(
 225                 "remote object implements illegal remote interface", e);
 226         }
 227         if (stub instanceof RemoteStub) {
 228             setSkeleton(impl);
 229         }
 230 
 231         Target target =
 232             new Target(impl, this, stub, ref.getObjID(), permanent);
 233         ref.exportObject(target);
 234         hashToMethod_Map = hashToMethod_Maps.get(implClass);
 235         return stub;
 236     }
 237 
 238     /**
 239      * Return the hostname of the current client.  When called from a
 240      * thread actively handling a remote method invocation the
 241      * hostname of the client is returned.
 242      * @exception ServerNotActiveException If called outside of servicing
 243      * a remote method invocation.
 244      */
 245     public String getClientHost() throws ServerNotActiveException {
 246         return TCPTransport.getClientHost();
 247     }
 248 
 249     /**
 250      * Discovers and sets the appropriate skeleton for the impl.
 251      */
 252     public void setSkeleton(Remote impl) throws RemoteException {
 253         if (!withoutSkeletons.containsKey(impl.getClass())) {
 254             try {
 255                 skel = Util.createSkeleton(impl);
 256             } catch (SkeletonNotFoundException e) {
 257                 /*
 258                  * Ignore exception for skeleton class not found, because a
 259                  * skeleton class is not necessary with the 1.2 stub protocol.
 260                  * Remember that this impl's class does not have a skeleton
 261                  * class so we don't waste time searching for it again.
 262                  */
 263                 withoutSkeletons.put(impl.getClass(), null);
 264             }
 265         }
 266     }
 267 
 268     /**
 269      * Call to dispatch to the remote object (on the server side).
 270      * The up-call to the server and the marshalling of return result
 271      * (or exception) should be handled before returning from this
 272      * method.
 273      * @param obj the target remote object for the call
 274      * @param call the "remote call" from which operation and
 275      * method arguments can be obtained.
 276      * @exception IOException If unable to marshal return result or
 277      * release input or output streams
 278      */
 279     public void dispatch(Remote obj, RemoteCall call) throws IOException {
 280         // positive operation number in 1.1 stubs;
 281         // negative version number in 1.2 stubs and beyond...
 282         int num;
 283         long op;
 284 
 285         try {
 286             // read remote call header
 287             ObjectInput in;
 288             try {
 289                 in = call.getInputStream();
 290                 num = in.readInt();
 291             } catch (Exception readEx) {
 292                 throw new UnmarshalException("error unmarshalling call header",
 293                                              readEx);
 294             }
 295             if (num >= 0) {
 296                 if (skel != null) {
 297                     oldDispatch(obj, call, num);
 298                     return;
 299                 } else {
 300                     throw new UnmarshalException(
 301                         "skeleton class not found but required " +
 302                         "for client version");
 303                 }
 304             }
 305             try {
 306                 op = in.readLong();
 307             } catch (Exception readEx) {
 308                 throw new UnmarshalException("error unmarshalling call header",
 309                         readEx);
 310             }
 311 
 312             /*
 313              * Since only system classes (with null class loaders) will be on
 314              * the execution stack during parameter unmarshalling for the 1.2
 315              * stub protocol, tell the MarshalInputStream not to bother trying
 316              * to resolve classes using its superclasses's default method of
 317              * consulting the first non-null class loader on the stack.
 318              */
 319             MarshalInputStream marshalStream = (MarshalInputStream) in;
 320             marshalStream.skipDefaultResolveClass();
 321 
 322             Method method = hashToMethod_Map.get(op);
 323             if (method == null) {
 324                 throw new UnmarshalException("unrecognized method hash: " +
 325                     "method not supported by remote object");
 326             }
 327 
 328             // if calls are being logged, write out object id and operation
 329             logCall(obj, method);
 330 
 331             // unmarshal parameters
 332             Class<?>[] types = method.getParameterTypes();
 333             Object[] params = new Object[types.length];
 334 
 335             try {
 336                 unmarshalCustomCallData(in);
 337                 // Unmarshal the parameters
 338                 for (int i = 0; i < types.length; i++) {
 339                     params[i] = unmarshalValue(types[i], in);
 340                 }
 341 
 342             } catch (AccessException aex) {
 343                 // For compatibility, AccessException is not wrapped in UnmarshalException
 344                 // disable saving any refs in the inputStream for GC
 345                 ((StreamRemoteCall) call).discardPendingRefs();
 346                 throw aex;
 347             } catch (java.io.IOException | ClassNotFoundException e) {
 348                 // disable saving any refs in the inputStream for GC
 349                 ((StreamRemoteCall) call).discardPendingRefs();
 350                 throw new UnmarshalException(
 351                     "error unmarshalling arguments", e);
 352             } finally {
 353                 call.releaseInputStream();
 354             }
 355 
 356             // make upcall on remote object
 357             Object result;
 358             try {
 359                 result = method.invoke(obj, params);
 360             } catch (InvocationTargetException e) {
 361                 throw e.getTargetException();
 362             }
 363 
 364             // marshal return value
 365             try {
 366                 ObjectOutput out = call.getResultStream(true);
 367                 Class<?> rtype = method.getReturnType();
 368                 if (rtype != void.class) {
 369                     marshalValue(rtype, result, out);
 370                 }
 371             } catch (IOException ex) {
 372                 throw new MarshalException("error marshalling return", ex);
 373                 /*
 374                  * This throw is problematic because when it is caught below,
 375                  * we attempt to marshal it back to the client, but at this
 376                  * point, a "normal return" has already been indicated,
 377                  * so marshalling an exception will corrupt the stream.
 378                  * This was the case with skeletons as well; there is no
 379                  * immediately obvious solution without a protocol change.
 380                  */
 381             }
 382         } catch (Throwable e) {
 383             Throwable origEx = e;
 384             logCallException(e);
 385 
 386             ObjectOutput out = call.getResultStream(false);
 387             if (e instanceof Error) {
 388                 e = new ServerError(
 389                     "Error occurred in server thread", (Error) e);
 390             } else if (e instanceof RemoteException) {
 391                 e = new ServerException(
 392                     "RemoteException occurred in server thread",
 393                     (Exception) e);
 394             }
 395             if (suppressStackTraces) {
 396                 clearStackTraces(e);
 397             }
 398             out.writeObject(e);
 399 
 400             // AccessExceptions should cause Transport.serviceCall
 401             // to flag the connection as unusable.
 402             if (origEx instanceof AccessException) {
 403                 throw new IOException("Connection is not reusable", origEx);
 404             }
 405         } finally {
 406             call.releaseInputStream(); // in case skeleton doesn't
 407             call.releaseOutputStream();
 408         }
 409     }
 410 
 411     /**
 412      * Sets a filter for invocation arguments, if a filter has been set.
 413      * Called by dispatch before the arguments are read.
 414      */
 415     protected void unmarshalCustomCallData(ObjectInput in)
 416             throws IOException, ClassNotFoundException {
 417         if (filter != null &&
 418                 in instanceof ObjectInputStream) {
 419             // Set the filter on the stream
 420             ObjectInputStream ois = (ObjectInputStream) in;
 421 
 422             AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
 423                 ois.setObjectInputFilter(filter);
 424                 return null;
 425             });
 426         }
 427     }
 428 
 429     /**
 430      * Handle server-side dispatch using the RMI 1.1 stub/skeleton
 431      * protocol, given a non-negative operation number that has
 432      * already been read from the call stream.
 433      * Exceptions are handled by the caller to be sent to the remote client.
 434      *
 435      * @param obj the target remote object for the call
 436      * @param call the "remote call" from which operation and
 437      * method arguments can be obtained.
 438      * @param op the operation number
 439      * @throws Exception if unable to marshal return result or
 440      * release input or output streams
 441      */
 442     private void oldDispatch(Remote obj, RemoteCall call, int op)
 443         throws Exception
 444     {
 445         long hash;              // hash for matching stub with skeleton
 446 
 447         // read remote call header
 448         ObjectInput in;
 449         in = call.getInputStream();
 450         try {
 451             Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
 452             if (clazz.isAssignableFrom(skel.getClass())) {
 453                 ((MarshalInputStream)in).useCodebaseOnly();
 454             }
 455         } catch (ClassNotFoundException ignore) { }
 456 
 457         try {
 458             hash = in.readLong();
 459         } catch (Exception ioe) {
 460             throw new UnmarshalException("error unmarshalling call header", ioe);
 461         }
 462 
 463         // if calls are being logged, write out object id and operation
 464         logCall(obj, skel.getOperations()[op]);
 465         unmarshalCustomCallData(in);
 466         // dispatch to skeleton for remote object
 467         skel.dispatch(obj, call, op, hash);
 468     }
 469 
 470     /**
 471      * Clear the stack trace of the given Throwable by replacing it with
 472      * an empty StackTraceElement array, and do the same for all of its
 473      * chained causative exceptions.
 474      */
 475     public static void clearStackTraces(Throwable t) {
 476         StackTraceElement[] empty = new StackTraceElement[0];
 477         while (t != null) {
 478             t.setStackTrace(empty);
 479             t = t.getCause();
 480         }
 481     }
 482 
 483     /**
 484      * Log the details of an incoming call.  The method parameter is either of
 485      * type java.lang.reflect.Method or java.rmi.server.Operation.
 486      */
 487     private void logCall(Remote obj, Object method) {
 488         if (callLog.isLoggable(Log.VERBOSE)) {
 489             String clientHost;
 490             try {
 491                 clientHost = getClientHost();
 492             } catch (ServerNotActiveException snae) {
 493                 clientHost = "(local)"; // shouldn't happen
 494             }
 495             callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
 496                               obj.getClass().getName() +
 497                               ref.getObjID().toString() + ": " +
 498                               method + "]");
 499         }
 500     }
 501 
 502     /**
 503      * Log the exception detail of an incoming call.
 504      */
 505     private void logCallException(Throwable e) {
 506         // if calls are being logged, log them
 507         if (callLog.isLoggable(Log.BRIEF)) {
 508             String clientHost = "";
 509             try {
 510                 clientHost = "[" + getClientHost() + "] ";
 511             } catch (ServerNotActiveException snae) {
 512             }
 513             callLog.log(Log.BRIEF, clientHost + "exception: ", e);
 514         }
 515 
 516         // write exceptions (only) to System.err if desired
 517         if (wantExceptionLog) {
 518             java.io.PrintStream log = System.err;
 519             synchronized (log) {
 520                 log.println();
 521                 log.println("Exception dispatching call to " +
 522                             ref.getObjID() + " in thread \"" +
 523                             Thread.currentThread().getName() +
 524                             "\" at " + (new Date()) + ":");
 525                 e.printStackTrace(log);
 526             }
 527         }
 528     }
 529 
 530     /**
 531      * Returns the class of the ref type to be serialized.
 532      */
 533     public String getRefClass(ObjectOutput out) {
 534         return "UnicastServerRef";
 535     }
 536 
 537     /**
 538      * Return the client remote reference for this remoteRef.
 539      * In the case of a client RemoteRef "this" is the answer.
 540      * For a server remote reference, a client side one will have to
 541      * found or created.
 542      */
 543     protected RemoteRef getClientRef() {
 544         return new UnicastRef(ref);
 545     }
 546 
 547     /**
 548      * Write out external representation for remote ref.
 549      */
 550     public void writeExternal(ObjectOutput out) throws IOException {
 551     }
 552 
 553     /**
 554      * Read in external representation for remote ref.
 555      * @exception ClassNotFoundException If the class for an object
 556      * being restored cannot be found.
 557      */
 558     public void readExternal(ObjectInput in)
 559         throws IOException, ClassNotFoundException
 560     {
 561         // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
 562         ref = null;
 563         skel = null;
 564     }
 565 
 566 
 567     /**
 568      * A weak hash map, mapping classes to hash maps that map method
 569      * hashes to method objects.
 570      **/
 571     private static class HashToMethod_Maps
 572         extends WeakClassHashMap<Map<Long,Method>>
 573     {
 574         HashToMethod_Maps() {}
 575 
 576         protected Map<Long,Method> computeValue(Class<?> remoteClass) {
 577             Map<Long,Method> map = new HashMap<>();
 578             for (Class<?> cl = remoteClass;
 579                  cl != null;
 580                  cl = cl.getSuperclass())
 581             {
 582                 for (Class<?> intf : cl.getInterfaces()) {
 583                     if (Remote.class.isAssignableFrom(intf)) {
 584                         for (Method method : intf.getMethods()) {
 585                             final Method m = method;
 586                             /*
 587                              * Set this Method object to override language
 588                              * access checks so that the dispatcher can invoke
 589                              * methods from non-public remote interfaces.
 590                              */
 591                             AccessController.doPrivileged(
 592                                 new PrivilegedAction<Void>() {
 593                                 public Void run() {
 594                                     m.setAccessible(true);
 595                                     return null;
 596                                 }
 597                             });
 598                             map.put(Util.computeMethodHash(m), m);
 599                         }
 600                     }
 601                 }
 602             }
 603             return map;
 604         }
 605     }
 606 
 607 }