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
26package sun.rmi.server;
27
28import java.io.IOException;
29import java.io.ObjectInput;
30import java.io.ObjectInputFilter;
31import java.io.ObjectInputStream;
32import java.io.ObjectOutput;
33import java.io.ObjectStreamClass;
34import java.lang.reflect.InvocationTargetException;
35import java.lang.reflect.Method;
36import java.rmi.AccessException;
37import java.rmi.MarshalException;
38import java.rmi.Remote;
39import java.rmi.RemoteException;
40import java.rmi.ServerError;
41import java.rmi.ServerException;
42import java.rmi.UnmarshalException;
43import java.rmi.server.ExportException;
44import java.rmi.server.RemoteCall;
45import java.rmi.server.RemoteRef;
46import java.rmi.server.RemoteStub;
47import java.rmi.server.ServerNotActiveException;
48import java.rmi.server.ServerRef;
49import java.rmi.server.Skeleton;
50import java.rmi.server.SkeletonNotFoundException;
51import java.security.AccessController;
52import java.security.PrivilegedAction;
53import java.util.Collections;
54import java.util.Date;
55import java.util.HashMap;
56import java.util.Map;
57import java.util.WeakHashMap;
58import java.util.concurrent.atomic.AtomicInteger;
59import sun.rmi.runtime.Log;
60import sun.rmi.transport.LiveRef;
61import sun.rmi.transport.StreamRemoteCall;
62import sun.rmi.transport.Target;
63import 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")
79public 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 (num >= 0) {
297                if (skel != null) {
298                    oldDispatch(obj, call, num);
299                    return;
300                } else {
301                    throw new UnmarshalException(
302                        "skeleton class not found but required " +
303                        "for client version");
304                }
305            }
306            try {
307                op = in.readLong();
308            } catch (Exception readEx) {
309                throw new UnmarshalException("error unmarshalling call header",
310                        readEx);
311            }
312
313            /*
314             * Since only system classes (with null class loaders) will be on
315             * the execution stack during parameter unmarshalling for the 1.2
316             * stub protocol, tell the MarshalInputStream not to bother trying
317             * to resolve classes using its superclasses's default method of
318             * consulting the first non-null class loader on the stack.
319             */
320            MarshalInputStream marshalStream = (MarshalInputStream) in;
321            marshalStream.skipDefaultResolveClass();
322
323            Method method = hashToMethod_Map.get(op);
324            if (method == null) {
325                throw new UnmarshalException("unrecognized method hash: " +
326                    "method not supported by remote object");
327            }
328
329            // if calls are being logged, write out object id and operation
330            logCall(obj, method);
331
332            // unmarshal parameters
333            Object[] params = null;
334
335            try {
336                unmarshalCustomCallData(in);
337                params = unmarshalParameters(obj, method, marshalStream);
338            } catch (AccessException aex) {
339                // For compatibility, AccessException is not wrapped in UnmarshalException
340                // disable saving any refs in the inputStream for GC
341                ((StreamRemoteCall) call).discardPendingRefs();
342                throw aex;
343            } catch (java.io.IOException | ClassNotFoundException e) {
344                // disable saving any refs in the inputStream for GC
345                ((StreamRemoteCall) call).discardPendingRefs();
346                throw new UnmarshalException(
347                    "error unmarshalling arguments", e);
348            } finally {
349                call.releaseInputStream();
350            }
351
352            // make upcall on remote object
353            Object result;
354            try {
355                result = method.invoke(obj, params);
356            } catch (InvocationTargetException e) {
357                throw e.getTargetException();
358            }
359
360            // marshal return value
361            try {
362                ObjectOutput out = call.getResultStream(true);
363                Class<?> rtype = method.getReturnType();
364                if (rtype != void.class) {
365                    marshalValue(rtype, result, out);
366                }
367            } catch (IOException ex) {
368                throw new MarshalException("error marshalling return", ex);
369                /*
370                 * This throw is problematic because when it is caught below,
371                 * we attempt to marshal it back to the client, but at this
372                 * point, a "normal return" has already been indicated,
373                 * so marshalling an exception will corrupt the stream.
374                 * This was the case with skeletons as well; there is no
375                 * immediately obvious solution without a protocol change.
376                 */
377            }
378        } catch (Throwable e) {
379            Throwable origEx = e;
380            logCallException(e);
381
382            ObjectOutput out = call.getResultStream(false);
383            if (e instanceof Error) {
384                e = new ServerError(
385                    "Error occurred in server thread", (Error) e);
386            } else if (e instanceof RemoteException) {
387                e = new ServerException(
388                    "RemoteException occurred in server thread",
389                    (Exception) e);
390            }
391            if (suppressStackTraces) {
392                clearStackTraces(e);
393            }
394            out.writeObject(e);
395
396            // AccessExceptions should cause Transport.serviceCall
397            // to flag the connection as unusable.
398            if (origEx instanceof AccessException) {
399                throw new IOException("Connection is not reusable", origEx);
400            }
401        } finally {
402            call.releaseInputStream(); // in case skeleton doesn't
403            call.releaseOutputStream();
404        }
405    }
406
407    /**
408     * Sets a filter for invocation arguments, if a filter has been set.
409     * Called by dispatch before the arguments are read.
410     */
411    protected void unmarshalCustomCallData(ObjectInput in)
412            throws IOException, ClassNotFoundException {
413        if (filter != null &&
414                in instanceof ObjectInputStream) {
415            // Set the filter on the stream
416            ObjectInputStream ois = (ObjectInputStream) in;
417
418            AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
419                ois.setObjectInputFilter(filter);
420                return null;
421            });
422        }
423    }
424
425    /**
426     * Handle server-side dispatch using the RMI 1.1 stub/skeleton
427     * protocol, given a non-negative operation number that has
428     * already been read from the call stream.
429     * Exceptions are handled by the caller to be sent to the remote client.
430     *
431     * @param obj the target remote object for the call
432     * @param call the "remote call" from which operation and
433     * method arguments can be obtained.
434     * @param op the operation number
435     * @throws Exception if unable to marshal return result or
436     * release input or output streams
437     */
438    private void oldDispatch(Remote obj, RemoteCall call, int op)
439        throws Exception
440    {
441        long hash;              // hash for matching stub with skeleton
442
443        // read remote call header
444        ObjectInput in;
445        in = call.getInputStream();
446        try {
447            Class<?> clazz = Class.forName("sun.rmi.transport.DGCImpl_Skel");
448            if (clazz.isAssignableFrom(skel.getClass())) {
449                ((MarshalInputStream)in).useCodebaseOnly();
450            }
451        } catch (ClassNotFoundException ignore) { }
452
453        try {
454            hash = in.readLong();
455        } catch (Exception ioe) {
456            throw new UnmarshalException("error unmarshalling call header", ioe);
457        }
458
459        // if calls are being logged, write out object id and operation
460        logCall(obj, skel.getOperations()[op]);
461        unmarshalCustomCallData(in);
462        // dispatch to skeleton for remote object
463        skel.dispatch(obj, call, op, hash);
464    }
465
466    /**
467     * Clear the stack trace of the given Throwable by replacing it with
468     * an empty StackTraceElement array, and do the same for all of its
469     * chained causative exceptions.
470     */
471    public static void clearStackTraces(Throwable t) {
472        StackTraceElement[] empty = new StackTraceElement[0];
473        while (t != null) {
474            t.setStackTrace(empty);
475            t = t.getCause();
476        }
477    }
478
479    /**
480     * Log the details of an incoming call.  The method parameter is either of
481     * type java.lang.reflect.Method or java.rmi.server.Operation.
482     */
483    private void logCall(Remote obj, Object method) {
484        if (callLog.isLoggable(Log.VERBOSE)) {
485            String clientHost;
486            try {
487                clientHost = getClientHost();
488            } catch (ServerNotActiveException snae) {
489                clientHost = "(local)"; // shouldn't happen
490            }
491            callLog.log(Log.VERBOSE, "[" + clientHost + ": " +
492                              obj.getClass().getName() +
493                              ref.getObjID().toString() + ": " +
494                              method + "]");
495        }
496    }
497
498    /**
499     * Log the exception detail of an incoming call.
500     */
501    private void logCallException(Throwable e) {
502        // if calls are being logged, log them
503        if (callLog.isLoggable(Log.BRIEF)) {
504            String clientHost = "";
505            try {
506                clientHost = "[" + getClientHost() + "] ";
507            } catch (ServerNotActiveException snae) {
508            }
509            callLog.log(Log.BRIEF, clientHost + "exception: ", e);
510        }
511
512        // write exceptions (only) to System.err if desired
513        if (wantExceptionLog) {
514            java.io.PrintStream log = System.err;
515            synchronized (log) {
516                log.println();
517                log.println("Exception dispatching call to " +
518                            ref.getObjID() + " in thread \"" +
519                            Thread.currentThread().getName() +
520                            "\" at " + (new Date()) + ":");
521                e.printStackTrace(log);
522            }
523        }
524    }
525
526    /**
527     * Returns the class of the ref type to be serialized.
528     */
529    public String getRefClass(ObjectOutput out) {
530        return "UnicastServerRef";
531    }
532
533    /**
534     * Return the client remote reference for this remoteRef.
535     * In the case of a client RemoteRef "this" is the answer.
536     * For a server remote reference, a client side one will have to
537     * found or created.
538     */
539    protected RemoteRef getClientRef() {
540        return new UnicastRef(ref);
541    }
542
543    /**
544     * Write out external representation for remote ref.
545     */
546    public void writeExternal(ObjectOutput out) throws IOException {
547    }
548
549    /**
550     * Read in external representation for remote ref.
551     * @exception ClassNotFoundException If the class for an object
552     * being restored cannot be found.
553     */
554    public void readExternal(ObjectInput in)
555        throws IOException, ClassNotFoundException
556    {
557        // object is re-exported elsewhere (e.g., by UnicastRemoteObject)
558        ref = null;
559        skel = null;
560    }
561
562
563    /**
564     * A weak hash map, mapping classes to hash maps that map method
565     * hashes to method objects.
566     **/
567    private static class HashToMethod_Maps
568        extends WeakClassHashMap<Map<Long,Method>>
569    {
570        HashToMethod_Maps() {}
571
572        protected Map<Long,Method> computeValue(Class<?> remoteClass) {
573            Map<Long,Method> map = new HashMap<>();
574            for (Class<?> cl = remoteClass;
575                 cl != null;
576                 cl = cl.getSuperclass())
577            {
578                for (Class<?> intf : cl.getInterfaces()) {
579                    if (Remote.class.isAssignableFrom(intf)) {
580                        for (Method method : intf.getMethods()) {
581                            final Method m = method;
582                            /*
583                             * Set this Method object to override language
584                             * access checks so that the dispatcher can invoke
585                             * methods from non-public remote interfaces.
586                             */
587                            AccessController.doPrivileged(
588                                new PrivilegedAction<Void>() {
589                                public Void run() {
590                                    m.setAccessible(true);
591                                    return null;
592                                }
593                            });
594                            map.put(Util.computeMethodHash(m), m);
595                        }
596                    }
597                }
598            }
599            return map;
600        }
601    }
602
603    /**
604     * Unmarshal parameters for the given method of the given instance over
605     * the given marshalinputstream. Perform any necessary checks.
606     */
607    private Object[] unmarshalParameters(Object obj, Method method, MarshalInputStream in)
608    throws IOException, ClassNotFoundException {
609        return (obj instanceof DeserializationChecker) ?
610            unmarshalParametersChecked((DeserializationChecker)obj, method, in) :
611            unmarshalParametersUnchecked(method, in);
612    }
613
614    /**
615     * Unmarshal parameters for the given method of the given instance over
616     * the given marshalinputstream. Do not perform any additional checks.
617     */
618    private Object[] unmarshalParametersUnchecked(Method method, ObjectInput in)
619    throws IOException, ClassNotFoundException {
620        Class<?>[] types = method.getParameterTypes();
621        Object[] params = new Object[types.length];
622        for (int i = 0; i < types.length; i++) {
623            params[i] = unmarshalValue(types[i], in);
624        }
625        return params;
626    }
627
628    /**
629     * Unmarshal parameters for the given method of the given instance over
630     * the given marshalinputstream. Do perform all additional checks.
631     */
632    private Object[] unmarshalParametersChecked(
633        DeserializationChecker checker,
634        Method method, MarshalInputStream in)
635    throws IOException, ClassNotFoundException {
636        int callID = methodCallIDCount.getAndIncrement();
637        MyChecker myChecker = new MyChecker(checker, method, callID);
638        in.setStreamChecker(myChecker);
639        try {
640            Class<?>[] types = method.getParameterTypes();
641            Object[] values = new Object[types.length];
642            for (int i = 0; i < types.length; i++) {
643                myChecker.setIndex(i);
644                values[i] = unmarshalValue(types[i], in);
645            }
646            myChecker.end(callID);
647            return values;
648        } finally {
649            in.setStreamChecker(null);
650        }
651    }
652
653    private static class MyChecker implements MarshalInputStream.StreamChecker {
654        private final DeserializationChecker descriptorCheck;
655        private final Method method;
656        private final int callID;
657        private int parameterIndex;
658
659        MyChecker(DeserializationChecker descriptorCheck, Method method, int callID) {
660            this.descriptorCheck = descriptorCheck;
661            this.method = method;
662            this.callID = callID;
663        }
664
665        @Override
666        public void validateDescriptor(ObjectStreamClass descriptor) {
667            descriptorCheck.check(method, descriptor, parameterIndex, callID);
668        }
669
670        @Override
671        public void checkProxyInterfaceNames(String[] ifaces) {
672            descriptorCheck.checkProxyClass(method, ifaces, parameterIndex, callID);
673        }
674
675        void setIndex(int parameterIndex) {
676            this.parameterIndex = parameterIndex;
677        }
678
679        void end(int callId) {
680            descriptorCheck.end(callId);
681        }
682    }
683}
684