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