1 /*
   2  * Copyright (c) 1996, 2019, 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.Operation;
  44 import java.rmi.server.RemoteCall;
  45 import java.rmi.server.RemoteRef;
  46 import java.rmi.server.RemoteStub;
  47 import java.rmi.server.ServerNotActiveException;
  48 import java.rmi.server.ServerRef;
  49 import java.rmi.server.Skeleton;
  50 import java.rmi.server.SkeletonNotFoundException;
  51 import java.security.AccessController;
  52 import java.security.PrivilegedAction;
  53 import java.util.Collections;
  54 import java.util.Date;
  55 import java.util.HashMap;
  56 import java.util.Map;
  57 import java.util.WeakHashMap;
  58 import java.util.concurrent.atomic.AtomicInteger;
  59 import sun.rmi.runtime.Log;
  60 import sun.rmi.transport.LiveRef;
  61 import sun.rmi.transport.StreamRemoteCall;
  62 import sun.rmi.transport.Target;
  63 import sun.rmi.transport.tcp.TCPTransport;
  64 
  65 /**
  66  * UnicastServerRef implements the remote reference layer server-side
  67  * behavior for remote objects exported with the "UnicastRef" reference
  68  * type.
  69  * If an {@link ObjectInputFilter ObjectInputFilter} is supplied it is
  70  * invoked during deserialization to filter the arguments,
  71  * otherwise the default filter of {@link ObjectInputStream ObjectInputStream}
  72  * applies.
  73  *
  74  * @author  Ann Wollrath
  75  * @author  Roger Riggs
  76  * @author  Peter Jones
  77  */
  78 @SuppressWarnings("deprecation")
  79 public class UnicastServerRef extends UnicastRef
  80     implements ServerRef, Dispatcher
  81 {
  82     /** value of server call log property */
  83     public static final boolean logCalls = AccessController.doPrivileged(
  84         (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("java.rmi.server.logCalls"));
  85 
  86     /** server call log */
  87     public static final Log callLog =
  88         Log.getLog("sun.rmi.server.call", "RMI", logCalls);
  89 
  90     // use serialVersionUID from JDK 1.2.2 for interoperability
  91     private static final long serialVersionUID = -7384275867073752268L;
  92 
  93     /** flag to enable writing exceptions to System.err */
  94     private static final boolean wantExceptionLog =
  95         AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
  96             Boolean.getBoolean("sun.rmi.server.exceptionTrace"));
  97 
  98     private boolean forceStubUse = false;
  99 
 100     /**
 101      * flag to remove server-side stack traces before marshalling
 102      * exceptions thrown by remote invocations to this VM
 103      */
 104     private static final boolean suppressStackTraces =
 105         AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
 106             Boolean.getBoolean("sun.rmi.server.suppressStackTraces"));
 107 
 108     /**
 109      * skeleton to dispatch remote calls through, for 1.1 stub protocol
 110      * (may be null if stub class only uses 1.2 stub protocol)
 111      */
 112     private transient Skeleton skel;
 113 
 114     // The ObjectInputFilter for checking the invocation arguments
 115     private final transient ObjectInputFilter filter;
 116 
 117     /** maps method hash to Method object for each remote method */
 118     private transient Map<Long,Method> hashToMethod_Map = null;
 119 
 120     /**
 121      * A weak hash map, mapping classes to hash maps that map method
 122      * hashes to method objects.
 123      **/
 124     private static final WeakClassHashMap<Map<Long,Method>> hashToMethod_Maps =
 125         new HashToMethod_Maps();
 126 
 127     /** cache of impl classes that have no corresponding skeleton class */
 128     private static final Map<Class<?>,?> withoutSkeletons =
 129         Collections.synchronizedMap(new WeakHashMap<Class<?>,Void>());
 130 
 131     private final AtomicInteger methodCallIDCount = new AtomicInteger(0);
 132 
 133     /**
 134      * Create a new (empty) Unicast server remote reference.
 135      * The filter is null to defer to the  default ObjectInputStream filter, if any.
 136      */
 137     public UnicastServerRef() {
 138         this.filter = null;
 139     }
 140 
 141     /**
 142      * Construct a Unicast server remote reference for a specified
 143      * liveRef.
 144      * The filter is null to defer to the  default ObjectInputStream filter, if any.
 145      */
 146     public UnicastServerRef(LiveRef ref) {
 147         super(ref);
 148         this.filter = null;
 149     }
 150 
 151     /**
 152      * Construct a Unicast server remote reference for a specified
 153      * liveRef and filter.
 154      */
 155     public UnicastServerRef(LiveRef ref, ObjectInputFilter filter) {
 156         super(ref);
 157         this.filter = filter;
 158     }
 159 
 160     /**
 161      * Construct a Unicast server remote reference to be exported
 162      * on the specified port.
 163      */
 164     public UnicastServerRef(int port) {
 165         super(new LiveRef(port));
 166         this.filter = null;
 167     }
 168 
 169     /**
 170      * Constructs a UnicastServerRef to be exported on an
 171      * anonymous port (i.e., 0) and that uses a pregenerated stub class
 172      * (NOT a dynamic proxy instance) if 'forceStubUse' is 'true'.
 173      *
 174      * This constructor is only called by the method
 175      * UnicastRemoteObject.exportObject(Remote) passing 'true' for
 176      * 'forceStubUse'.  The UnicastRemoteObject.exportObject(Remote) method
 177      * returns RemoteStub, so it must ensure that the stub for the
 178      * exported object is an instance of a pregenerated stub class that
 179      * extends RemoteStub (instead of an instance of a dynamic proxy class
 180      * which is not an instance of RemoteStub).
 181      **/
 182     public UnicastServerRef(boolean forceStubUse) {
 183         this(0);
 184         this.forceStubUse = forceStubUse;
 185     }
 186 
 187     /**
 188      * With the addition of support for dynamic proxies as stubs, this
 189      * method is obsolete because it returns RemoteStub instead of the more
 190      * general Remote.  It should not be called.  It sets the
 191      * 'forceStubUse' flag to true so that the stub for the exported object
 192      * is forced to be an instance of the pregenerated stub class, which
 193      * extends RemoteStub.
 194      *
 195      * Export this object, create the skeleton and stubs for this
 196      * dispatcher.  Create a stub based on the type of the impl,
 197      * initialize it with the appropriate remote reference. Create the
 198      * target defined by the impl, dispatcher (this) and stub.
 199      * Export that target via the Ref.
 200      **/
 201     public RemoteStub exportObject(Remote impl, Object data)
 202         throws RemoteException
 203     {
 204         forceStubUse = true;
 205         return (RemoteStub) exportObject(impl, data, false);
 206     }
 207 
 208     /**
 209      * Export this object, create the skeleton and stubs for this
 210      * dispatcher.  Create a stub based on the type of the impl,
 211      * initialize it with the appropriate remote reference. Create the
 212      * target defined by the impl, dispatcher (this) and stub.
 213      * Export that target via the Ref.
 214      */
 215     public Remote exportObject(Remote impl, Object data,
 216                                boolean permanent)
 217         throws RemoteException
 218     {
 219         Class<?> implClass = impl.getClass();
 220         Remote stub;
 221 
 222         try {
 223             stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
 224         } catch (IllegalArgumentException e) {
 225             throw new ExportException(
 226                 "remote object implements illegal remote interface", e);
 227         }
 228         if (stub instanceof RemoteStub) {
 229             setSkeleton(impl);
 230         }
 231 
 232         Target target =
 233             new Target(impl, this, stub, ref.getObjID(), permanent);
 234         ref.exportObject(target);
 235         hashToMethod_Map = hashToMethod_Maps.get(implClass);
 236         return stub;
 237     }
 238 
 239     /**
 240      * Return the hostname of the current client.  When called from a
 241      * thread actively handling a remote method invocation the
 242      * hostname of the client is returned.
 243      * @exception ServerNotActiveException If called outside of servicing
 244      * a remote method invocation.
 245      */
 246     public String getClientHost() throws ServerNotActiveException {
 247         return TCPTransport.getClientHost();
 248     }
 249 
 250     /**
 251      * Discovers and sets the appropriate skeleton for the impl.
 252      */
 253     public void setSkeleton(Remote impl) throws RemoteException {
 254         if (!withoutSkeletons.containsKey(impl.getClass())) {
 255             try {
 256                 skel = Util.createSkeleton(impl);
 257             } catch (SkeletonNotFoundException e) {
 258                 /*
 259                  * Ignore exception for skeleton class not found, because a
 260                  * skeleton class is not necessary with the 1.2 stub protocol.
 261                  * Remember that this impl's class does not have a skeleton
 262                  * class so we don't waste time searching for it again.
 263                  */
 264                 withoutSkeletons.put(impl.getClass(), null);
 265             }
 266         }
 267     }
 268 
 269     /**
 270      * Call to dispatch to the remote object (on the server side).
 271      * The up-call to the server and the marshalling of return result
 272      * (or exception) should be handled before returning from this
 273      * method.
 274      * @param obj the target remote object for the call
 275      * @param call the "remote call" from which operation and
 276      * method arguments can be obtained.
 277      * @exception IOException If unable to marshal return result or
 278      * release input or output streams
 279      */
 280     public void dispatch(Remote obj, RemoteCall call) throws IOException {
 281         // positive operation number in 1.1 stubs;
 282         // negative version number in 1.2 stubs and beyond...
 283         int num;
 284         long op;
 285 
 286         try {
 287             // read remote call header
 288             ObjectInput in;
 289             try {
 290                 in = call.getInputStream();
 291                 num = in.readInt();
 292             } catch (Exception readEx) {
 293                 throw new UnmarshalException("error unmarshalling call header",
 294                                              readEx);
 295             }
 296             if (skel != null) {
 297                 // If there is a skeleton, use it
 298                     oldDispatch(obj, call, num);
 299                     return;
 300 
 301             } else if (num >= 0){
 302                 throw new UnmarshalException(
 303                         "skeleton class not found but required for client version");
 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 or negative method hash
 432      * that has 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         Operation[] operations = skel.getOperations();
 465         logCall(obj, op >= 0 && op < operations.length ?  operations[op] : "op: " + op);
 466         unmarshalCustomCallData(in);
 467         // dispatch to skeleton for remote object
 468         skel.dispatch(obj, call, op, hash);
 469     }
 470 
 471     /**
 472      * Clear the stack trace of the given Throwable by replacing it with
 473      * an empty StackTraceElement array, and do the same for all of its
 474      * chained causative exceptions.
 475      */
 476     public static void clearStackTraces(Throwable t) {
 477         StackTraceElement[] empty = new StackTraceElement[0];
 478         while (t != null) {
 479             t.setStackTrace(empty);
 480             t = t.getCause();
 481         }
 482     }
 483 
 484     /**
 485      * Log the details of an incoming call.  The method parameter is either of
 486      * type java.lang.reflect.Method or java.rmi.server.Operation.
 487      */
 488     private void logCall(Remote obj, Object method) {
 489         if (callLog.isLoggable(Log.VERBOSE)) {
 490             String clientHost;
 491             try {
 492                 clientHost = getClientHost();
 493             } catch (ServerNotActiveException snae) {
 494                 clientHost = "(local)"; // shouldn't happen
 495             }
 496             callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
 497                               obj.getClass().getName() +
 498                               ref.getObjID().toString() + ": " +
 499                               method + "]");
 500         }
 501     }
 502 
 503     /**
 504      * Log the exception detail of an incoming call.
 505      */
 506     private void logCallException(Throwable e) {
 507         // if calls are being logged, log them
 508         if (callLog.isLoggable(Log.BRIEF)) {
 509             String clientHost = "";
 510             try {
 511                 clientHost = "[" + getClientHost() + "] ";
 512             } catch (ServerNotActiveException snae) {
 513             }
 514             callLog.log(Log.BRIEF, clientHost + "exception: ", e);
 515         }
 516 
 517         // write exceptions (only) to System.err if desired
 518         if (wantExceptionLog) {
 519             java.io.PrintStream log = System.err;
 520             synchronized (log) {
 521                 log.println();
 522                 log.println("Exception dispatching call to " +
 523                             ref.getObjID() + " in thread \"" +
 524                             Thread.currentThread().getName() +
 525                             "\" at " + (new Date()) + ":");
 526                 e.printStackTrace(log);
 527             }
 528         }
 529     }
 530 
 531     /**
 532      * Returns the class of the ref type to be serialized.
 533      */
 534     public String getRefClass(ObjectOutput out) {
 535         return "UnicastServerRef";
 536     }
 537 
 538     /**
 539      * Return the client remote reference for this remoteRef.
 540      * In the case of a client RemoteRef "this" is the answer.
 541      * For a server remote reference, a client side one will have to
 542      * found or created.
 543      */
 544     protected RemoteRef getClientRef() {
 545         return new UnicastRef(ref);
 546     }
 547 
 548     /**
 549      * Write out external representation for remote ref.
 550      */
 551     public void writeExternal(ObjectOutput out) throws IOException {
 552     }
 553 
 554     /**
 555      * Read in external representation for remote ref.
 556      * @exception ClassNotFoundException If the class for an object
 557      * being restored cannot be found.
 558      */
 559     public void readExternal(ObjectInput in)
 560         throws IOException, ClassNotFoundException
 561     {
 562         // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
 563         ref = null;
 564         skel = null;
 565     }
 566 
 567 
 568     /**
 569      * A weak hash map, mapping classes to hash maps that map method
 570      * hashes to method objects.
 571      **/
 572     private static class HashToMethod_Maps
 573         extends WeakClassHashMap<Map<Long,Method>>
 574     {
 575         HashToMethod_Maps() {}
 576 
 577         protected Map<Long,Method> computeValue(Class<?> remoteClass) {
 578             Map<Long,Method> map = new HashMap<>();
 579             for (Class<?> cl = remoteClass;
 580                  cl != null;
 581                  cl = cl.getSuperclass())
 582             {
 583                 for (Class<?> intf : cl.getInterfaces()) {
 584                     if (Remote.class.isAssignableFrom(intf)) {
 585                         for (Method method : intf.getMethods()) {
 586                             final Method m = method;
 587                             /*
 588                              * Set this Method object to override language
 589                              * access checks so that the dispatcher can invoke
 590                              * methods from non-public remote interfaces.
 591                              */
 592                             AccessController.doPrivileged(
 593                                 new PrivilegedAction<Void>() {
 594                                 public Void run() {
 595                                     m.setAccessible(true);
 596                                     return null;
 597                                 }
 598                             });
 599                             map.put(Util.computeMethodHash(m), m);
 600                         }
 601                     }
 602                 }
 603             }
 604             return map;
 605         }
 606     }
 607 
 608 }