1 /*
2  * Copyright (c) 1998, 2018, 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 #include "util.h"
27 #include "utf_util.h"
28 #include "transport.h"
29 #include "debugLoop.h"
30 #include "sys.h"
31 
32 static jdwpTransportEnv *transport = NULL;
33 static unsigned transportVersion = JDWPTRANSPORT_VERSION_1_0;
34 
35 static jrawMonitorID listenerLock;
36 static jrawMonitorID sendLock;
37 
38 /*
39  * data structure used for passing transport info from thread to thread
40  */
41 typedef struct TransportInfo {
42     char *name;
43     jdwpTransportEnv *transport;
44     char *address;
45     long timeout;
46     char *allowed_peers;
47     unsigned transportVersion;
48 } TransportInfo;
49 
50 static struct jdwpTransportCallback callback = {jvmtiAllocate, jvmtiDeallocate};
51 
freeTransportInfo(TransportInfo * info)52 static void freeTransportInfo(TransportInfo *info) {
53     if (info) {
54         jvmtiDeallocate(info->name);
55         jvmtiDeallocate(info->address);
56         jvmtiDeallocate(info->allowed_peers);
57         jvmtiDeallocate(info);
58     }
59 }
60 
61 /*
62  * Print the last transport error
63  */
64 static void
printLastError(jdwpTransportEnv * t,jdwpTransportError err)65 printLastError(jdwpTransportEnv *t, jdwpTransportError err)
66 {
67     char  *msg;
68     jbyte *utf8msg;
69     jdwpTransportError rv;
70 
71     msg     = NULL;
72     utf8msg = NULL;
73     rv = (*t)->GetLastError(t, &msg); /* This is a platform encoded string */
74     if ( msg != NULL ) {
75         int len;
76         int maxlen;
77 
78         /* Convert this string to UTF8 */
79         len = (int)strlen(msg);
80         maxlen = len+len/2+2; /* Should allow for plenty of room */
81         utf8msg = (jbyte*)jvmtiAllocate(maxlen+1);
82         if (utf8msg != NULL) {
83            (void)utf8FromPlatform(msg, len, utf8msg, maxlen+1);
84         }
85     }
86     if (rv == JDWPTRANSPORT_ERROR_NONE) {
87         ERROR_MESSAGE(("transport error %d: %s",err, utf8msg));
88     } else if ( msg!=NULL ) {
89         ERROR_MESSAGE(("transport error %d: %s",err, utf8msg));
90     } else {
91         ERROR_MESSAGE(("transport error %d: %s",err, "UNKNOWN"));
92     }
93     jvmtiDeallocate(msg);
94     jvmtiDeallocate(utf8msg);
95 }
96 
97 /* Find OnLoad symbol */
98 static jdwpTransport_OnLoad_t
findTransportOnLoad(void * handle)99 findTransportOnLoad(void *handle)
100 {
101     jdwpTransport_OnLoad_t onLoad;
102 
103     onLoad = (jdwpTransport_OnLoad_t)NULL;
104     if (handle == NULL) {
105         return onLoad;
106     }
107 #if defined(_WIN32) && !defined(_WIN64)
108     onLoad = (jdwpTransport_OnLoad_t)
109                  dbgsysFindLibraryEntry(handle, "_jdwpTransport_OnLoad@16");
110     if (onLoad != NULL) {
111         return onLoad;
112     }
113 #endif
114     onLoad = (jdwpTransport_OnLoad_t)
115                  dbgsysFindLibraryEntry(handle, "jdwpTransport_OnLoad");
116     return onLoad;
117 }
118 
119 /* Load transport library (directory=="" means do system search) */
120 static void *
loadTransportLibrary(const char * libdir,const char * name)121 loadTransportLibrary(const char *libdir, const char *name)
122 {
123     char buf[MAXPATHLEN*2+100];
124 #ifndef STATIC_BUILD
125     void *handle;
126     char libname[MAXPATHLEN+2];
127     const char *plibdir;
128 
129     /* Convert libdir from UTF-8 to platform encoding */
130     plibdir = NULL;
131     if ( libdir != NULL ) {
132         int  len;
133 
134         len = (int)strlen(libdir);
135         (void)utf8ToPlatform((jbyte*)libdir, len, buf, (int)sizeof(buf));
136         plibdir = buf;
137     }
138 
139     /* Construct library name (simple name or full path) */
140     dbgsysBuildLibName(libname, sizeof(libname), plibdir, name);
141     if (strlen(libname) == 0) {
142         return NULL;
143     }
144 
145     /* dlopen (unix) / LoadLibrary (windows) the transport library */
146     handle = dbgsysLoadLibrary(libname, buf, sizeof(buf));
147     return handle;
148 #else
149     return (dbgsysLoadLibrary(NULL, buf, sizeof(buf)));
150 #endif
151 }
152 
153 /*
154  * loadTransport() is adapted from loadJVMHelperLib() in
155  * JDK 1.2 javai.c v1.61
156  */
157 static jdwpError
loadTransport(const char * name,TransportInfo * info)158 loadTransport(const char *name, TransportInfo *info)
159 {
160     JNIEnv                 *env;
161     jdwpTransport_OnLoad_t  onLoad;
162     void                   *handle;
163     const char             *libdir;
164 
165     /* Make sure library name is not empty */
166     if (name == NULL) {
167         ERROR_MESSAGE(("library name is empty"));
168         return JDWP_ERROR(TRANSPORT_LOAD);
169     }
170     if (info == NULL) {
171         ERROR_MESSAGE(("internal error: info should not be NULL"));
172         return JDWP_ERROR(TRANSPORT_LOAD);
173     }
174 
175     /* First, look in sun.boot.library.path. This should find the standard
176      *  dt_socket and dt_shmem transport libraries, or any library
177      *  that was delivered with the J2SE.
178      *  Note: Since 6819213 fixed, Java property sun.boot.library.path can
179      *  contain multiple paths. Dll_dir is the first entry and
180      *  -Dsun.boot.library.path entries are appended.
181      */
182     libdir = gdata->property_sun_boot_library_path;
183     if (libdir == NULL) {
184         ERROR_MESSAGE(("Java property sun.boot.library.path is not set"));
185         return JDWP_ERROR(TRANSPORT_LOAD);
186     }
187     handle = loadTransportLibrary(libdir, name);
188     if (handle == NULL) {
189         /* Second, look along the path used by the native dlopen/LoadLibrary
190          *  functions. This should effectively try and load the simple
191          *  library name, which will cause the default system library
192          *  search technique to happen.
193          *  We should only reach here if the transport library wasn't found
194          *  in the J2SE directory, e.g. it's a custom transport library
195          *  not installed in the J2SE like dt_socket and dt_shmem is.
196          *
197          *  Note: Why not use java.library.path? Several reasons:
198          *        a) This matches existing agentlib search
199          *        b) These are technically not JNI libraries
200          */
201         handle = loadTransportLibrary("", name);
202     }
203 
204     /* See if a library was found with this name */
205     if (handle == NULL) {
206         ERROR_MESSAGE(("transport library not found: %s", name));
207         return JDWP_ERROR(TRANSPORT_LOAD);
208     }
209 
210     /* Find the onLoad address */
211     onLoad = findTransportOnLoad(handle);
212     if (onLoad == NULL) {
213         ERROR_MESSAGE(("transport library missing onLoad entry: %s", name));
214         return JDWP_ERROR(TRANSPORT_LOAD);
215     }
216 
217     /* Get transport interface */
218     env = getEnv();
219     if (env != NULL) {
220         jdwpTransportEnv *t = NULL;
221         JavaVM           *jvm = NULL;
222         jint              rc;
223         size_t            i;
224         /* If a new version is added here, update 'case JNI_EVERSION' below. */
225         jint supported_versions[2] = {JDWPTRANSPORT_VERSION_1_1, JDWPTRANSPORT_VERSION_1_0};
226 
227         JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm);
228 
229         /* Try version 1.1 first, fallback to 1.0 on error */
230         for (i = 0; i < sizeof(supported_versions)/sizeof(jint); ++i) {
231             rc = (*onLoad)(jvm, &callback, supported_versions[i], &t);
232             if (rc != JNI_EVERSION) {
233                 info->transportVersion = supported_versions[i];
234                 break;
235             }
236         }
237 
238         if (rc != JNI_OK) {
239             switch (rc) {
240                 case JNI_ENOMEM :
241                     ERROR_MESSAGE(("insufficient memory to complete initialization"));
242                     break;
243 
244                 case JNI_EVERSION :
245                     ERROR_MESSAGE(("transport doesn't recognize all supported versions: "
246                                    "{ 1_1, 1_0 }"));
247                     break;
248 
249                 case JNI_EEXIST :
250                     ERROR_MESSAGE(("transport doesn't support multiple environments"));
251                     break;
252 
253                 default:
254                     ERROR_MESSAGE(("unrecognized error %d from transport", rc));
255                     break;
256             }
257 
258             return JDWP_ERROR(TRANSPORT_INIT);
259         }
260 
261         /* Store transport version to global variable to be able to
262          * set correct transport version for subsequent connect,
263          * even if info is already deallocated.
264          */
265         transportVersion = info->transportVersion;
266         info->transport = t;
267     } else {
268         return JDWP_ERROR(TRANSPORT_LOAD);
269     }
270 
271     return JDWP_ERROR(NONE);
272 }
273 
274 static void
connectionInitiated(jdwpTransportEnv * t)275 connectionInitiated(jdwpTransportEnv *t)
276 {
277     jint isValid = JNI_FALSE;
278 
279     debugMonitorEnter(listenerLock);
280 
281     /*
282      * Don't allow a connection until initialization is complete
283      */
284     debugInit_waitInitComplete();
285 
286     /* Are we the first transport to get a connection? */
287 
288     if (transport == NULL) {
289         transport = t;
290         isValid = JNI_TRUE;
291     } else {
292         if (transport == t) {
293             /* connected with the same transport as before */
294             isValid = JNI_TRUE;
295         } else {
296             /*
297              * Another transport got a connection - multiple transports
298              * not fully supported yet so shouldn't get here.
299              */
300             (*t)->Close(t);
301             JDI_ASSERT(JNI_FALSE);
302         }
303     }
304 
305     if (isValid) {
306         debugMonitorNotifyAll(listenerLock);
307     }
308 
309     debugMonitorExit(listenerLock);
310 
311     if (isValid) {
312         debugLoop_run();
313     }
314 
315 }
316 
317 /*
318  * Set the transport property (sun.jdwp.listenerAddress) to the
319  * specified value.
320  */
321 static void
setTransportProperty(JNIEnv * env,char * value)322 setTransportProperty(JNIEnv* env, char* value) {
323     char* prop_value = (value == NULL) ? "" : value;
324     setAgentPropertyValue(env, "sun.jdwp.listenerAddress", prop_value);
325 }
326 
327 void
transport_waitForConnection(void)328 transport_waitForConnection(void)
329 {
330     /*
331      * If the VM is suspended on debugger initialization, we wait
332      * for a connection before continuing. This ensures that all
333      * events are delivered to the debugger. (We might as well do this
334      * this since the VM won't continue until a remote debugger attaches
335      * and resumes it.) If not suspending on initialization, we must
336      * just drop any packets (i.e. events) so that the VM can continue
337      * to run. The debugger may not attach until much later.
338      */
339     if (debugInit_suspendOnInit()) {
340         debugMonitorEnter(listenerLock);
341         while (transport == NULL) {
342             debugMonitorWait(listenerLock);
343         }
344         debugMonitorExit(listenerLock);
345     }
346 }
347 
348 static void JNICALL
acceptThread(jvmtiEnv * jvmti_env,JNIEnv * jni_env,void * arg)349 acceptThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
350 {
351     TransportInfo *info;
352     jdwpTransportEnv *t;
353     jdwpTransportError rc;
354 
355     LOG_MISC(("Begin accept thread"));
356 
357     info = (TransportInfo*)arg;
358     t = info->transport;
359     rc = (*t)->Accept(t, info->timeout, 0);
360 
361     /* System property no longer needed */
362     setTransportProperty(jni_env, NULL);
363     /* TransportInfo data no longer needed */
364     freeTransportInfo(info);
365 
366     if (rc != JDWPTRANSPORT_ERROR_NONE) {
367         /*
368          * If accept fails it probably means a timeout, or another fatal error
369          * We thus exit the VM after stopping the listener.
370          */
371         printLastError(t, rc);
372         (*t)->StopListening(t);
373         EXIT_ERROR(JVMTI_ERROR_NONE, "could not connect, timeout or fatal error");
374     } else {
375         (*t)->StopListening(t);
376         connectionInitiated(t);
377     }
378 
379     LOG_MISC(("End accept thread"));
380 }
381 
382 static void JNICALL
attachThread(jvmtiEnv * jvmti_env,JNIEnv * jni_env,void * arg)383 attachThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
384 {
385     TransportInfo *info = (TransportInfo*)arg;
386     jdwpTransportEnv *t = info->transport;
387 
388     /* TransportInfo data no longer needed */
389     freeTransportInfo(info);
390 
391     LOG_MISC(("Begin attach thread"));
392     connectionInitiated(t);
393     LOG_MISC(("End attach thread"));
394 }
395 
396 void
transport_initialize(void)397 transport_initialize(void)
398 {
399     transport = NULL;
400     listenerLock = debugMonitorCreate("JDWP Transport Listener Monitor");
401     sendLock = debugMonitorCreate("JDWP Transport Send Monitor");
402 }
403 
404 void
transport_reset(void)405 transport_reset(void)
406 {
407     /*
408      * Reset the transport by closing any listener (will silently fail
409      * with JDWPTRANSPORT_ERROR_ILLEGAL_STATE if not listening), and
410      * closing any connection (will also fail silently if not
411      * connected).
412      *
413      * Note: There's an assumption here that we don't yet support
414      * multiple transports. When we do then we need a clear transition
415      * from the current transport to the new transport.
416      */
417     if (transport != NULL) {
418         setTransportProperty(getEnv(), NULL);
419         (*transport)->StopListening(transport);
420         (*transport)->Close(transport);
421     }
422 }
423 
424 static jdwpError
launch(char * command,char * name,char * address)425 launch(char *command, char *name, char *address)
426 {
427     jint rc;
428     char *buf;
429     char *commandLine;
430     int  len;
431 
432     /* Construct complete command line (all in UTF-8) */
433     commandLine = jvmtiAllocate((int)strlen(command) +
434                                  (int)strlen(name) +
435                                  (int)strlen(address) + 3);
436     if (commandLine == NULL) {
437         return JDWP_ERROR(OUT_OF_MEMORY);
438     }
439     (void)strcpy(commandLine, command);
440     (void)strcat(commandLine, " ");
441     (void)strcat(commandLine, name);
442     (void)strcat(commandLine, " ");
443     (void)strcat(commandLine, address);
444 
445     /* Convert commandLine from UTF-8 to platform encoding */
446     len = (int)strlen(commandLine);
447     buf = jvmtiAllocate(len*3+3);
448     if (buf == NULL) {
449         jvmtiDeallocate(commandLine);
450         return JDWP_ERROR(OUT_OF_MEMORY);
451     }
452     (void)utf8ToPlatform((jbyte*)commandLine, len, buf, len*3+3);
453 
454     /* Exec commandLine */
455     rc = dbgsysExec(buf);
456 
457     /* Free up buffers */
458     jvmtiDeallocate(buf);
459     jvmtiDeallocate(commandLine);
460 
461     /* And non-zero exit status means we had an error */
462     if (rc != SYS_OK) {
463         return JDWP_ERROR(TRANSPORT_INIT);
464     }
465     return JDWP_ERROR(NONE);
466 }
467 
468 jdwpError
transport_startTransport(jboolean isServer,char * name,char * address,long timeout,char * allowed_peers)469 transport_startTransport(jboolean isServer, char *name, char *address,
470                          long timeout, char *allowed_peers)
471 {
472     jvmtiStartFunction func;
473     char threadName[MAXPATHLEN + 100];
474     jint err;
475     jdwpError serror;
476     jdwpTransportConfiguration cfg = {0};
477     TransportInfo *info;
478     jdwpTransportEnv *trans;
479 
480     info = jvmtiAllocate(sizeof(*info));
481     if (info == NULL) {
482         return JDWP_ERROR(OUT_OF_MEMORY);
483     }
484 
485     info->transport = transport;
486     info->transportVersion = transportVersion;
487     info->name = NULL;
488     info->address = NULL;
489     info->allowed_peers = NULL;
490 
491     /*
492      * If the transport is already loaded then use it
493      * Note: We're assuming here that we don't support multiple
494      * transports - when we do then we need to handle the case
495      * where the transport library only supports a single environment.
496      * That probably means we have a bag a transport environments
497      * to correspond to the transports bag.
498      */
499     if (info->transport == NULL) {
500         serror = loadTransport(name, info);
501         if (serror != JDWP_ERROR(NONE)) {
502             freeTransportInfo(info);
503             return serror;
504         }
505     }
506 
507     // Cache the value
508     trans = info->transport;
509 
510     if (isServer) {
511         char *retAddress;
512         char *launchCommand;
513         jvmtiError error;
514         int len;
515         char* prop_value;
516 
517         info->timeout = timeout;
518 
519         info->name = jvmtiAllocate((int)strlen(name)+1);
520         if (info->name == NULL) {
521             serror = JDWP_ERROR(OUT_OF_MEMORY);
522             goto handleError;
523         }
524         (void)strcpy(info->name, name);
525 
526         if (address != NULL) {
527             info->address = jvmtiAllocate((int)strlen(address)+1);
528             if (info->address == NULL) {
529                 serror = JDWP_ERROR(OUT_OF_MEMORY);
530                 goto handleError;
531             }
532             (void)strcpy(info->address, address);
533         }
534 
535         if (info->transportVersion == JDWPTRANSPORT_VERSION_1_0) {
536             if (allowed_peers != NULL) {
537                 ERROR_MESSAGE(("Allow parameter is specified but transport doesn't support it"));
538                 serror = JDWP_ERROR(TRANSPORT_INIT);
539                 goto handleError;
540             }
541         } else {
542             /* Memory is allocated only for transport versions > 1.0
543              * as the version 1.0 does not support the 'allow' option.
544              */
545             if (allowed_peers != NULL) {
546                 info->allowed_peers = jvmtiAllocate((int)strlen(allowed_peers) + 1);
547                 if (info->allowed_peers == NULL) {
548                     serror = JDWP_ERROR(OUT_OF_MEMORY);
549                     goto handleError;
550                 }
551                 (void)strcpy(info->allowed_peers, allowed_peers);
552             }
553             cfg.allowed_peers = info->allowed_peers;
554             err = (*trans)->SetTransportConfiguration(trans, &cfg);
555             if (err != JDWPTRANSPORT_ERROR_NONE) {
556                 printLastError(trans, err);
557                 serror = JDWP_ERROR(TRANSPORT_INIT);
558                 goto handleError;
559             }
560         }
561 
562         err = (*trans)->StartListening(trans, address, &retAddress);
563         if (err != JDWPTRANSPORT_ERROR_NONE) {
564             printLastError(trans, err);
565             serror = JDWP_ERROR(TRANSPORT_INIT);
566             goto handleError;
567         }
568 
569         /*
570          * Record listener address in a system property
571          */
572         len = (int)strlen(name) + (int)strlen(retAddress) + 2; /* ':' and '\0' */
573         prop_value = (char*)jvmtiAllocate(len);
574         if (prop_value == NULL) {
575             serror = JDWP_ERROR(OUT_OF_MEMORY);
576             goto handleError;
577         }
578         strcpy(prop_value, name);
579         strcat(prop_value, ":");
580         strcat(prop_value, retAddress);
581         setTransportProperty(getEnv(), prop_value);
582         jvmtiDeallocate(prop_value);
583 
584 
585         (void)strcpy(threadName, "JDWP Transport Listener: ");
586         (void)strcat(threadName, name);
587 
588         func = &acceptThread;
589         error = spawnNewThread(func, (void*)info, threadName);
590         if (error != JVMTI_ERROR_NONE) {
591             serror = map2jdwpError(error);
592             goto handleError;
593         }
594 
595         /* reset info - it will be deallocated by acceptThread */
596         info = NULL;
597 
598         launchCommand = debugInit_launchOnInit();
599         if (launchCommand != NULL) {
600             serror = launch(launchCommand, name, retAddress);
601             if (serror != JDWP_ERROR(NONE)) {
602                 goto handleError;
603             }
604         } else {
605             if ( ! gdata->quiet ) {
606                 TTY_MESSAGE(("Listening for transport %s at address: %s",
607                     name, retAddress));
608             }
609         }
610         return JDWP_ERROR(NONE);
611 
612 handleError:
613         freeTransportInfo(info);
614     } else {
615         /*
616          * Note that we don't attempt to do a launch here. Launching
617          * is currently supported only in server mode.
618          */
619 
620         /*
621          * If we're connecting to another process, there shouldn't be
622          * any concurrent listens, so its ok if we block here in this
623          * thread, waiting for the attach to finish.
624          */
625          err = (*trans)->Attach(trans, address, timeout, 0);
626          if (err != JDWPTRANSPORT_ERROR_NONE) {
627              printLastError(trans, err);
628              serror = JDWP_ERROR(TRANSPORT_INIT);
629              /* The name, address and allowed_peers fields in 'info'
630               * are not allocated in the non-server case so
631               * they do not need to be freed. */
632              freeTransportInfo(info);
633              return serror;
634          }
635 
636          /*
637           * Start the transport loop in a separate thread
638           */
639          (void)strcpy(threadName, "JDWP Transport Listener: ");
640          (void)strcat(threadName, name);
641 
642          func = &attachThread;
643          err = spawnNewThread(func, (void*)info, threadName);
644          serror = map2jdwpError(err);
645     }
646     return serror;
647 }
648 
649 void
transport_close(void)650 transport_close(void)
651 {
652     if ( transport != NULL ) {
653         (*transport)->Close(transport);
654     }
655 }
656 
657 jboolean
transport_is_open(void)658 transport_is_open(void)
659 {
660     jboolean is_open = JNI_FALSE;
661 
662     if ( transport != NULL ) {
663         is_open = (*transport)->IsOpen(transport);
664     }
665     return is_open;
666 }
667 
668 jint
transport_sendPacket(jdwpPacket * packet)669 transport_sendPacket(jdwpPacket *packet)
670 {
671     jdwpTransportError err = JDWPTRANSPORT_ERROR_NONE;
672     jint rc = 0;
673 
674     if (transport != NULL) {
675         if ( (*transport)->IsOpen(transport) ) {
676             debugMonitorEnter(sendLock);
677             err = (*transport)->WritePacket(transport, packet);
678             debugMonitorExit(sendLock);
679         }
680         if (err != JDWPTRANSPORT_ERROR_NONE) {
681             if ((*transport)->IsOpen(transport)) {
682                 printLastError(transport, err);
683             }
684 
685             /*
686              * The users of transport_sendPacket except 0 for
687              * success; non-0 otherwise.
688              */
689             rc = (jint)-1;
690         }
691 
692     } /* else, bit bucket */
693 
694     return rc;
695 }
696 
697 jint
transport_receivePacket(jdwpPacket * packet)698 transport_receivePacket(jdwpPacket *packet)
699 {
700     jdwpTransportError err;
701 
702     err = (*transport)->ReadPacket(transport, packet);
703     if (err != JDWPTRANSPORT_ERROR_NONE) {
704         /*
705          * If transport has been closed return EOF
706          */
707         if (!(*transport)->IsOpen(transport)) {
708             packet->type.cmd.len = 0;
709             return 0;
710         }
711 
712         printLastError(transport, err);
713 
714         /*
715          * Users of transport_receivePacket expect 0 for success,
716          * non-0 otherwise.
717          */
718         return (jint)-1;
719     }
720     return 0;
721 }
722