1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * JDWP spy. This is a rearranged version of the JDWP code from the VM.
5 */
6 #include "Common.h"
7 #include "jdwp/JdwpConstants.h"
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <netinet/tcp.h>
17 #include <arpa/inet.h>
18 #include <netdb.h>
19 #include <time.h>
20 #include <errno.h>
21 #include <assert.h>
22
23 #define kInputBufferSize (256*1024)
24
25 #define kMagicHandshakeLen 14 /* "JDWP-Handshake" */
26 #define kJDWPHeaderLen 11
27 #define kJDWPFlagReply 0x80
28
29
30 /*
31 * Information about the remote end.
32 */
33 typedef struct Peer {
34 char label[2]; /* 'D' or 'V' */
35
36 int sock;
37 unsigned char inputBuffer[kInputBufferSize];
38 int inputCount;
39
40 bool awaitingHandshake; /* waiting for "JDWP-Handshake" */
41 } Peer;
42
43
44 /*
45 * Network state.
46 */
47 typedef struct NetState {
48 /* listen here for connection from debugger */
49 int listenSock;
50
51 /* connect here to contact VM */
52 struct in_addr vmAddr;
53 short vmPort;
54
55 Peer dbg;
56 Peer vm;
57 } NetState;
58
59 /*
60 * Function names.
61 */
62 typedef struct {
63 u1 cmdSet;
64 u1 cmd;
65 const char* descr;
66 } JdwpHandlerMap;
67
68 /*
69 * Map commands to names.
70 *
71 * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
72 * and 128-256 are vendor-defined.
73 */
74 static const JdwpHandlerMap gHandlerMap[] = {
75 /* VirtualMachine command set (1) */
76 { 1, 1, "VirtualMachine.Version" },
77 { 1, 2, "VirtualMachine.ClassesBySignature" },
78 { 1, 3, "VirtualMachine.AllClasses" },
79 { 1, 4, "VirtualMachine.AllThreads" },
80 { 1, 5, "VirtualMachine.TopLevelThreadGroups" },
81 { 1, 6, "VirtualMachine.Dispose" },
82 { 1, 7, "VirtualMachine.IDSizes" },
83 { 1, 8, "VirtualMachine.Suspend" },
84 { 1, 9, "VirtualMachine.Resume" },
85 { 1, 10, "VirtualMachine.Exit" },
86 { 1, 11, "VirtualMachine.CreateString" },
87 { 1, 12, "VirtualMachine.Capabilities" },
88 { 1, 13, "VirtualMachine.ClassPaths" },
89 { 1, 14, "VirtualMachine.DisposeObjects" },
90 { 1, 15, "VirtualMachine.HoldEvents" },
91 { 1, 16, "VirtualMachine.ReleaseEvents" },
92 { 1, 17, "VirtualMachine.CapabilitiesNew" },
93 { 1, 18, "VirtualMachine.RedefineClasses" },
94 { 1, 19, "VirtualMachine.SetDefaultStratum" },
95 { 1, 20, "VirtualMachine.AllClassesWithGeneric"},
96 { 1, 21, "VirtualMachine.InstanceCounts"},
97
98 /* ReferenceType command set (2) */
99 { 2, 1, "ReferenceType.Signature" },
100 { 2, 2, "ReferenceType.ClassLoader" },
101 { 2, 3, "ReferenceType.Modifiers" },
102 { 2, 4, "ReferenceType.Fields" },
103 { 2, 5, "ReferenceType.Methods" },
104 { 2, 6, "ReferenceType.GetValues" },
105 { 2, 7, "ReferenceType.SourceFile" },
106 { 2, 8, "ReferenceType.NestedTypes" },
107 { 2, 9, "ReferenceType.Status" },
108 { 2, 10, "ReferenceType.Interfaces" },
109 { 2, 11, "ReferenceType.ClassObject" },
110 { 2, 12, "ReferenceType.SourceDebugExtension" },
111 { 2, 13, "ReferenceType.SignatureWithGeneric" },
112 { 2, 14, "ReferenceType.FieldsWithGeneric" },
113 { 2, 15, "ReferenceType.MethodsWithGeneric" },
114 { 2, 16, "ReferenceType.Instances" },
115 { 2, 17, "ReferenceType.ClassFileVersion" },
116 { 2, 18, "ReferenceType.ConstantPool" },
117
118 /* ClassType command set (3) */
119 { 3, 1, "ClassType.Superclass" },
120 { 3, 2, "ClassType.SetValues" },
121 { 3, 3, "ClassType.InvokeMethod" },
122 { 3, 4, "ClassType.NewInstance" },
123
124 /* ArrayType command set (4) */
125 { 4, 1, "ArrayType.NewInstance" },
126
127 /* InterfaceType command set (5) */
128
129 /* Method command set (6) */
130 { 6, 1, "Method.LineTable" },
131 { 6, 2, "Method.VariableTable" },
132 { 6, 3, "Method.Bytecodes" },
133 { 6, 4, "Method.IsObsolete" },
134 { 6, 5, "Method.VariableTableWithGeneric" },
135
136 /* Field command set (8) */
137
138 /* ObjectReference command set (9) */
139 { 9, 1, "ObjectReference.ReferenceType" },
140 { 9, 2, "ObjectReference.GetValues" },
141 { 9, 3, "ObjectReference.SetValues" },
142 { 9, 4, "ObjectReference.UNUSED" },
143 { 9, 5, "ObjectReference.MonitorInfo" },
144 { 9, 6, "ObjectReference.InvokeMethod" },
145 { 9, 7, "ObjectReference.DisableCollection" },
146 { 9, 8, "ObjectReference.EnableCollection" },
147 { 9, 9, "ObjectReference.IsCollected" },
148 { 9, 10, "ObjectReference.ReferringObjects" },
149
150 /* StringReference command set (10) */
151 { 10, 1, "StringReference.Value" },
152
153 /* ThreadReference command set (11) */
154 { 11, 1, "ThreadReference.Name" },
155 { 11, 2, "ThreadReference.Suspend" },
156 { 11, 3, "ThreadReference.Resume" },
157 { 11, 4, "ThreadReference.Status" },
158 { 11, 5, "ThreadReference.ThreadGroup" },
159 { 11, 6, "ThreadReference.Frames" },
160 { 11, 7, "ThreadReference.FrameCount" },
161 { 11, 8, "ThreadReference.OwnedMonitors" },
162 { 11, 9, "ThreadReference.CurrentContendedMonitor" },
163 { 11, 10, "ThreadReference.Stop" },
164 { 11, 11, "ThreadReference.Interrupt" },
165 { 11, 12, "ThreadReference.SuspendCount" },
166 { 11, 13, "ThreadReference.OwnedMonitorsStackDepthInfo" },
167 { 11, 14, "ThreadReference.ForceEarlyReturn" },
168
169 /* ThreadGroupReference command set (12) */
170 { 12, 1, "ThreadGroupReference.Name" },
171 { 12, 2, "ThreadGroupReference.Parent" },
172 { 12, 3, "ThreadGroupReference.Children" },
173
174 /* ArrayReference command set (13) */
175 { 13, 1, "ArrayReference.Length" },
176 { 13, 2, "ArrayReference.GetValues" },
177 { 13, 3, "ArrayReference.SetValues" },
178
179 /* ClassLoaderReference command set (14) */
180 { 14, 1, "ArrayReference.VisibleClasses" },
181
182 /* EventRequest command set (15) */
183 { 15, 1, "EventRequest.Set" },
184 { 15, 2, "EventRequest.Clear" },
185 { 15, 3, "EventRequest.ClearAllBreakpoints" },
186
187 /* StackFrame command set (16) */
188 { 16, 1, "StackFrame.GetValues" },
189 { 16, 2, "StackFrame.SetValues" },
190 { 16, 3, "StackFrame.ThisObject" },
191 { 16, 4, "StackFrame.PopFrames" },
192
193 /* ClassObjectReference command set (17) */
194 { 17, 1, "ClassObjectReference.ReflectedType" },
195
196 /* Event command set (64) */
197 { 64, 100, "Event.Composite" },
198
199 /* DDMS */
200 { 199, 1, "DDMS.Chunk" },
201 };
202
203 /*
204 * Look up a command's name.
205 */
getCommandName(int cmdSet,int cmd)206 static const char* getCommandName(int cmdSet, int cmd)
207 {
208 for (int i = 0; i < (int) NELEM(gHandlerMap); i++) {
209 if (gHandlerMap[i].cmdSet == cmdSet &&
210 gHandlerMap[i].cmd == cmd)
211 {
212 return gHandlerMap[i].descr;
213 }
214 }
215
216 return "?UNKNOWN?";
217 }
218
219
220 void jdwpNetFree(NetState* netState); /* fwd */
221
222 /*
223 * Allocate state structure and bind to the listen port.
224 *
225 * Returns 0 on success.
226 */
jdwpNetStartup(unsigned short listenPort,const char * connectHost,unsigned short connectPort)227 NetState* jdwpNetStartup(unsigned short listenPort, const char* connectHost,
228 unsigned short connectPort)
229 {
230 NetState* netState = (NetState*) malloc(sizeof(*netState));
231 memset(netState, 0, sizeof(*netState));
232 netState->listenSock = -1;
233 netState->dbg.sock = netState->vm.sock = -1;
234
235 strcpy(netState->dbg.label, "D");
236 strcpy(netState->vm.label, "V");
237
238 /*
239 * Set up a socket to listen for connections from the debugger.
240 */
241
242 netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
243 if (netState->listenSock < 0) {
244 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
245 goto fail;
246 }
247
248 /* allow immediate re-use if we die */
249 {
250 int one = 1;
251 if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
252 sizeof(one)) < 0)
253 {
254 fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n",
255 strerror(errno));
256 goto fail;
257 }
258 }
259
260 struct sockaddr_in addr;
261 addr.sin_family = AF_INET;
262 addr.sin_port = htons(listenPort);
263 addr.sin_addr.s_addr = INADDR_ANY;
264
265 if (bind(netState->listenSock, (struct sockaddr*) &addr, sizeof(addr)) != 0)
266 {
267 fprintf(stderr, "attempt to bind to port %u failed: %s\n",
268 listenPort, strerror(errno));
269 goto fail;
270 }
271
272 fprintf(stderr, "+++ bound to port %u\n", listenPort);
273
274 if (listen(netState->listenSock, 5) != 0) {
275 fprintf(stderr, "Listen failed: %s\n", strerror(errno));
276 goto fail;
277 }
278
279 /*
280 * Do the hostname lookup for the VM.
281 */
282 struct hostent* pHost;
283
284 pHost = gethostbyname(connectHost);
285 if (pHost == NULL) {
286 fprintf(stderr, "Name lookup of '%s' failed: %s\n",
287 connectHost, strerror(h_errno));
288 goto fail;
289 }
290
291 netState->vmAddr = *((struct in_addr*) pHost->h_addr_list[0]);
292 netState->vmPort = connectPort;
293
294 fprintf(stderr, "+++ connect host resolved to %s\n",
295 inet_ntoa(netState->vmAddr));
296
297 return netState;
298
299 fail:
300 jdwpNetFree(netState);
301 return NULL;
302 }
303
304 /*
305 * Shut down JDWP listener. Don't free state.
306 *
307 * Note that "netState" may be partially initialized if "startup" failed.
308 */
jdwpNetShutdown(NetState * netState)309 void jdwpNetShutdown(NetState* netState)
310 {
311 int listenSock = netState->listenSock;
312 int dbgSock = netState->dbg.sock;
313 int vmSock = netState->vm.sock;
314
315 /* clear these out so it doesn't wake up and try to reuse them */
316 /* (important when multi-threaded) */
317 netState->listenSock = netState->dbg.sock = netState->vm.sock = -1;
318
319 if (listenSock >= 0) {
320 shutdown(listenSock, SHUT_RDWR);
321 close(listenSock);
322 }
323 if (dbgSock >= 0) {
324 shutdown(dbgSock, SHUT_RDWR);
325 close(dbgSock);
326 }
327 if (vmSock >= 0) {
328 shutdown(vmSock, SHUT_RDWR);
329 close(vmSock);
330 }
331 }
332
333 /*
334 * Shut down JDWP listener and free its state.
335 */
jdwpNetFree(NetState * netState)336 void jdwpNetFree(NetState* netState)
337 {
338 if (netState == NULL)
339 return;
340
341 jdwpNetShutdown(netState);
342 free(netState);
343 }
344
345 /*
346 * Disable the TCP Nagle algorithm, which delays transmission of outbound
347 * packets until the previous transmissions have been acked. JDWP does a
348 * lot of back-and-forth with small packets, so this may help.
349 */
setNoDelay(int fd)350 static int setNoDelay(int fd)
351 {
352 int cc, on = 1;
353
354 cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
355 assert(cc == 0);
356 return cc;
357 }
358
359 /*
360 * Accept a connection. This will block waiting for somebody to show up.
361 */
jdwpAcceptConnection(NetState * netState)362 bool jdwpAcceptConnection(NetState* netState)
363 {
364 struct sockaddr_in addr;
365 socklen_t addrlen;
366 int sock;
367
368 if (netState->listenSock < 0)
369 return false; /* you're not listening! */
370
371 assert(netState->dbg.sock < 0); /* must not already be talking */
372
373 addrlen = sizeof(addr);
374 do {
375 sock = accept(netState->listenSock, (struct sockaddr*) &addr, &addrlen);
376 if (sock < 0 && errno != EINTR) {
377 fprintf(stderr, "accept failed: %s\n", strerror(errno));
378 return false;
379 }
380 } while (sock < 0);
381
382 fprintf(stderr, "+++ accepted connection from %s:%u\n",
383 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
384
385 netState->dbg.sock = sock;
386 netState->dbg.awaitingHandshake = true;
387 netState->dbg.inputCount = 0;
388
389 setNoDelay(sock);
390
391 return true;
392 }
393
394 /*
395 * Close the connections to the debugger and VM.
396 *
397 * Reset the state so we're ready to receive a new connection.
398 */
jdwpCloseConnection(NetState * netState)399 void jdwpCloseConnection(NetState* netState)
400 {
401 if (netState->dbg.sock >= 0) {
402 fprintf(stderr, "+++ closing connection to debugger\n");
403 close(netState->dbg.sock);
404 netState->dbg.sock = -1;
405 }
406 if (netState->vm.sock >= 0) {
407 fprintf(stderr, "+++ closing connection to vm\n");
408 close(netState->vm.sock);
409 netState->vm.sock = -1;
410 }
411 }
412
413 /*
414 * Figure out if we have a full packet in the buffer.
415 */
haveFullPacket(Peer * pPeer)416 static bool haveFullPacket(Peer* pPeer)
417 {
418 long length;
419
420 if (pPeer->awaitingHandshake)
421 return (pPeer->inputCount >= kMagicHandshakeLen);
422
423 if (pPeer->inputCount < 4)
424 return false;
425
426 length = get4BE(pPeer->inputBuffer);
427 return (pPeer->inputCount >= length);
428 }
429
430 /*
431 * Consume bytes from the buffer.
432 *
433 * This would be more efficient with a circular buffer. However, we're
434 * usually only going to find one packet, which is trivial to handle.
435 */
consumeBytes(Peer * pPeer,int count)436 static void consumeBytes(Peer* pPeer, int count)
437 {
438 assert(count > 0);
439 assert(count <= pPeer->inputCount);
440
441 if (count == pPeer->inputCount) {
442 pPeer->inputCount = 0;
443 return;
444 }
445
446 memmove(pPeer->inputBuffer, pPeer->inputBuffer + count,
447 pPeer->inputCount - count);
448 pPeer->inputCount -= count;
449 }
450
451 /*
452 * Get the current time.
453 */
getCurrentTime(int * pMin,int * pSec)454 static void getCurrentTime(int* pMin, int* pSec)
455 {
456 time_t now;
457 struct tm* ptm;
458
459 now = time(NULL);
460 ptm = localtime(&now);
461 *pMin = ptm->tm_min;
462 *pSec = ptm->tm_sec;
463 }
464
465 /*
466 * Dump the contents of a packet to stdout.
467 */
dumpPacket(const unsigned char * packetBuf,const char * srcName,const char * dstName)468 static void dumpPacket(const unsigned char* packetBuf, const char* srcName,
469 const char* dstName)
470 {
471 const unsigned char* buf = packetBuf;
472 char prefix[3];
473 u4 length, id;
474 u1 flags, cmdSet=0, cmd=0;
475 JdwpError error = ERR_NONE;
476 bool reply;
477 int dataLen;
478
479 length = get4BE(buf+0);
480 id = get4BE(buf+4);
481 flags = get1(buf+8);
482 if ((flags & kJDWPFlagReply) != 0) {
483 reply = true;
484 error = static_cast<JdwpError>(get2BE(buf+9));
485 } else {
486 reply = false;
487 cmdSet = get1(buf+9);
488 cmd = get1(buf+10);
489 }
490
491 buf += kJDWPHeaderLen;
492 dataLen = length - (buf - packetBuf);
493
494 if (!reply) {
495 prefix[0] = srcName[0];
496 prefix[1] = '>';
497 } else {
498 prefix[0] = dstName[0];
499 prefix[1] = '<';
500 }
501 prefix[2] = '\0';
502
503 int min, sec;
504 getCurrentTime(&min, &sec);
505
506 if (!reply) {
507 printf("%s REQUEST dataLen=%-5u id=0x%08x flags=0x%02x cmd=%d/%d [%02d:%02d]\n",
508 prefix, dataLen, id, flags, cmdSet, cmd, min, sec);
509 printf("%s --> %s\n", prefix, getCommandName(cmdSet, cmd));
510 } else {
511 printf("%s REPLY dataLen=%-5u id=0x%08x flags=0x%02x err=%d (%s) [%02d:%02d]\n",
512 prefix, dataLen, id, flags, error, dvmJdwpErrorStr(error), min,sec);
513 }
514 if (dataLen > 0)
515 printHexDump2(buf, dataLen, prefix);
516 printf("%s ----------\n", prefix);
517 }
518
519 /*
520 * Handle a packet. Returns "false" if we encounter a connection-fatal error.
521 */
handlePacket(Peer * pDst,Peer * pSrc)522 static bool handlePacket(Peer* pDst, Peer* pSrc)
523 {
524 const unsigned char* buf = pSrc->inputBuffer;
525 u4 length;
526 u1 flags;
527 int cc;
528
529 length = get4BE(buf+0);
530 flags = get1(buf+9);
531
532 assert((int) length <= pSrc->inputCount);
533
534 dumpPacket(buf, pSrc->label, pDst->label);
535
536 cc = write(pDst->sock, buf, length);
537 if (cc != (int) length) {
538 fprintf(stderr, "Failed sending packet: %s\n", strerror(errno));
539 return false;
540 }
541 /*printf("*** wrote %d bytes from %c to %c\n",
542 cc, pSrc->label[0], pDst->label[0]);*/
543
544 consumeBytes(pSrc, length);
545 return true;
546 }
547
548 /*
549 * Handle incoming data. If we have a full packet in the buffer, process it.
550 */
handleIncoming(Peer * pWritePeer,Peer * pReadPeer)551 static bool handleIncoming(Peer* pWritePeer, Peer* pReadPeer)
552 {
553 if (haveFullPacket(pReadPeer)) {
554 if (pReadPeer->awaitingHandshake) {
555 printf("Handshake [%c]: %.14s\n",
556 pReadPeer->label[0], pReadPeer->inputBuffer);
557 if (write(pWritePeer->sock, pReadPeer->inputBuffer,
558 kMagicHandshakeLen) != kMagicHandshakeLen)
559 {
560 fprintf(stderr,
561 "+++ [%c] handshake write failed\n", pReadPeer->label[0]);
562 goto fail;
563 }
564 consumeBytes(pReadPeer, kMagicHandshakeLen);
565 pReadPeer->awaitingHandshake = false;
566 } else {
567 if (!handlePacket(pWritePeer, pReadPeer))
568 goto fail;
569 }
570 } else {
571 /*printf("*** %c not full yet\n", pReadPeer->label[0]);*/
572 }
573
574 return true;
575
576 fail:
577 return false;
578 }
579
580 /*
581 * Process incoming data. If no data is available, this will block until
582 * some arrives.
583 *
584 * Returns "false" on error (indicating that the connection has been severed).
585 */
jdwpProcessIncoming(NetState * netState)586 bool jdwpProcessIncoming(NetState* netState)
587 {
588 int cc;
589
590 assert(netState->dbg.sock >= 0);
591 assert(netState->vm.sock >= 0);
592
593 while (!haveFullPacket(&netState->dbg) && !haveFullPacket(&netState->vm)) {
594 /* read some more */
595 int highFd;
596 fd_set readfds;
597
598 highFd = (netState->dbg.sock > netState->vm.sock) ?
599 netState->dbg.sock+1 : netState->vm.sock+1;
600 FD_ZERO(&readfds);
601 FD_SET(netState->dbg.sock, &readfds);
602 FD_SET(netState->vm.sock, &readfds);
603
604 errno = 0;
605 cc = select(highFd, &readfds, NULL, NULL, NULL);
606 if (cc < 0) {
607 if (errno == EINTR) {
608 fprintf(stderr, "+++ EINTR on select\n");
609 continue;
610 }
611 fprintf(stderr, "+++ select failed: %s\n", strerror(errno));
612 goto fail;
613 }
614
615 if (FD_ISSET(netState->dbg.sock, &readfds)) {
616 cc = read(netState->dbg.sock,
617 netState->dbg.inputBuffer + netState->dbg.inputCount,
618 sizeof(netState->dbg.inputBuffer) - netState->dbg.inputCount);
619 if (cc < 0) {
620 if (errno == EINTR) {
621 fprintf(stderr, "+++ EINTR on read\n");
622 continue;
623 }
624 fprintf(stderr, "+++ dbg read failed: %s\n", strerror(errno));
625 goto fail;
626 }
627 if (cc == 0) {
628 if (sizeof(netState->dbg.inputBuffer) ==
629 netState->dbg.inputCount)
630 fprintf(stderr, "+++ debugger sent huge message\n");
631 else
632 fprintf(stderr, "+++ debugger disconnected\n");
633 goto fail;
634 }
635
636 /*printf("*** %d bytes from dbg\n", cc);*/
637 netState->dbg.inputCount += cc;
638 }
639
640 if (FD_ISSET(netState->vm.sock, &readfds)) {
641 cc = read(netState->vm.sock,
642 netState->vm.inputBuffer + netState->vm.inputCount,
643 sizeof(netState->vm.inputBuffer) - netState->vm.inputCount);
644 if (cc < 0) {
645 if (errno == EINTR) {
646 fprintf(stderr, "+++ EINTR on read\n");
647 continue;
648 }
649 fprintf(stderr, "+++ vm read failed: %s\n", strerror(errno));
650 goto fail;
651 }
652 if (cc == 0) {
653 if (sizeof(netState->vm.inputBuffer) ==
654 netState->vm.inputCount)
655 fprintf(stderr, "+++ vm sent huge message\n");
656 else
657 fprintf(stderr, "+++ vm disconnected\n");
658 goto fail;
659 }
660
661 /*printf("*** %d bytes from vm\n", cc);*/
662 netState->vm.inputCount += cc;
663 }
664 }
665
666 if (!handleIncoming(&netState->dbg, &netState->vm))
667 goto fail;
668 if (!handleIncoming(&netState->vm, &netState->dbg))
669 goto fail;
670
671 return true;
672
673 fail:
674 jdwpCloseConnection(netState);
675 return false;
676 }
677
678 /*
679 * Connect to the VM.
680 */
jdwpConnectToVm(NetState * netState)681 bool jdwpConnectToVm(NetState* netState)
682 {
683 struct sockaddr_in addr;
684 int sock = -1;
685
686 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
687 if (sock < 0) {
688 fprintf(stderr, "Socket create failed: %s\n", strerror(errno));
689 goto fail;
690 }
691
692 addr.sin_family = AF_INET;
693 addr.sin_addr = netState->vmAddr;
694 addr.sin_port = htons(netState->vmPort);
695 if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
696 fprintf(stderr, "Connection to %s:%u failed: %s\n",
697 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), strerror(errno));
698 goto fail;
699 }
700 fprintf(stderr, "+++ connected to VM %s:%u\n",
701 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
702
703 netState->vm.sock = sock;
704 netState->vm.awaitingHandshake = true;
705 netState->vm.inputCount = 0;
706
707 setNoDelay(netState->vm.sock);
708 return true;
709
710 fail:
711 if (sock >= 0)
712 close(sock);
713 return false;
714 }
715
716 /*
717 * Establish network connections and start things running.
718 *
719 * We wait for a new connection from the debugger. When one arrives we
720 * open a connection to the VM. If one side or the other goes away, we
721 * drop both ends and go back to listening.
722 */
run(const char * connectHost,int connectPort,int listenPort)723 int run(const char* connectHost, int connectPort, int listenPort)
724 {
725 NetState* state;
726
727 state = jdwpNetStartup(listenPort, connectHost, connectPort);
728 if (state == NULL)
729 return -1;
730
731 while (true) {
732 if (!jdwpAcceptConnection(state))
733 break;
734
735 if (jdwpConnectToVm(state)) {
736 while (true) {
737 if (!jdwpProcessIncoming(state))
738 break;
739 }
740 }
741
742 jdwpCloseConnection(state);
743 }
744
745 jdwpNetFree(state);
746
747 return 0;
748 }
749