3 /// Server protocol implementation
7 Copyright (C) 2009, Nicolas VIVIEN
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
27 #include "j_message.h"
28 #include "protostructs.h"
29 #include "record-internal.h"
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
47 namespace Barry
{ namespace JDWP
{
49 static void * acceptThread(void *data
);
52 JDWServer::JDWServer(Barry::Mode::JVMDebug
&device
,
53 const char *address
, int port
)
60 , targetrunning(false)
61 , printConsoleMessage(0)
63 SearchDebugFile(debugFileList
);
67 JDWServer::~JDWServer()
73 void JDWServer::SetPasswordDevice(string password
)
75 this->password
= password
;
79 void JDWServer::SetConsoleCallback(ConsoleCallbackType callback
)
81 printConsoleMessage
= callback
;
84 static const char* h_strerror(int code
)
86 // Codes and messages taken from the Linux gethostbyname(3) manpage
90 return "HOST_NOT_FOUND: The specified host is unknown";
93 return "NO_ADDRESS: The requested name is valid but does not have an IP address";
96 return "NO_RECOVERY: A non-recoverable name server error occurred";
99 return "TRY_AGAIN: A temporary error occurred on an authoritative name server. Try again later.";
102 return "Unknown network error code";
106 bool JDWServer::Start()
111 struct sockaddr_in sad
;
114 memset((char *) &sad
, '\0', sizeof(struct sockaddr_in
));
117 sad
.sin_addr
.s_addr
= INADDR_ANY
;
119 sad
.sin_addr
.s_addr
= inet_addr(address
.c_str());
121 if (sad
.sin_addr
.s_addr
== INADDR_NONE
) {
122 hp
= gethostbyname(address
.c_str());
125 std::ostringstream oss
;
126 oss
<< "JDWServer::Start: " << h_errno
<< h_strerror(h_errno
);
127 throw Barry::Error(oss
.str());
130 memcpy((char*) &sad
.sin_addr
, (char*) hp
->h_addr
, (size_t) hp
->h_length
);
134 sad
.sin_family
= AF_INET
;
135 sad
.sin_port
= htons((short) (port
& 0xFFFF));
138 sockfd
= socket(sad
.sin_family
, SOCK_STREAM
, 0);
141 throw Barry::ErrnoError("JDWServer::Start: Cannot open socket.", errno
);
145 rc
= bind(sockfd
, (struct sockaddr
*) &sad
, sizeof(sad
));
153 throw Barry::ErrnoError("JDWServer::Start: Cannot bind socket", code
);
157 if (listen(sockfd
, SOMAXCONN
) < 0) {
163 throw Barry::ErrnoError("JDWServer::Start: Cannot listen on socket", code
);
166 handler
.reset(new Thread(sockfd
, acceptThread
, (void*) this));
172 static void * acceptThread(void *data
)
176 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
178 s
= (JDWServer
*) data
;
181 if( s
->AcceptConnection() &&
182 s
->AttachToDevice() &&
183 s
->InitVisibleClassList() &&
187 s
->DetachFromDevice();
195 // Returns true if a new connection was accepted and established
196 bool JDWServer::AcceptConnection()
198 struct sockaddr_in addr
;
199 struct sockaddr
*sa
= (struct sockaddr
*) &addr
;
200 socklen_t addrlen
= sizeof(addr
);
202 acceptfd
= accept(sockfd
, sa
, &addrlen
);
206 fcntl(acceptfd
, F_SETFL
, O_NONBLOCK
);
211 bool JDWServer::AttachToDevice()
213 targetrunning
= false;
215 jvmdebug
->Open(password
.c_str());
218 jvmdebug
->Unknown01();
219 jvmdebug
->Unknown02();
220 jvmdebug
->Unknown03();
221 jvmdebug
->Unknown04();
222 jvmdebug
->Unknown05();
224 jvmdebug
->GetModulesList(modulesList
);
227 // Check debug info for each modules
228 JVMModulesList::const_iterator b
= modulesList
.begin();
229 for ( ; b
!= modulesList
.end(); b
++) {
230 JDG::CodInfo codInfo
;
232 const JVMModulesEntry
&entry
= *b
;
234 bool ret
= LoadDebugInfo(debugFileList
, entry
.UniqueID
, entry
.Name
, codInfo
);
237 appList
[entry
.UniqueID
].Load(codInfo
);
240 dout("No debug information found for '" << entry
.Name
);
241 dout("' (" << hex
<< setfill('0') << setw(8) << entry
.UniqueID
<< ")." << endl
)
249 void JDWServer::DetachFromDevice()
256 #define JDWP_HELLO_STRING "JDWP-Handshake"
260 bool JDWServer::Hello()
264 Barry::Data response
;
266 const size_t len
= strlen(JDWP_HELLO_STRING
);
268 JDWMessage
msg(acceptfd
);
271 ret
= msg
.Receive(response
);
275 size_t size
= response
.GetSize();
276 char *str
= (char *) response
.GetBuffer();
281 if (!strncmp(str
, JDWP_HELLO_STRING
, len
)) {
282 Data
command(JDWP_HELLO_STRING
, len
);
293 void JDWServer::Run()
296 JDWMessage
msg(acceptfd
);
300 MAKE_JDWPPACKET(rpack
, command
);
306 // Read JDWP message from device
307 int value
= jvmdebug
->GetConsoleMessage(str
);
313 ret
= jvmdebug
->GetStatus(status
);
316 // Read JDB message from host
317 msg
.Receive(command
);
319 if (command
.GetSize() > 0) {
321 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
323 if (command
.GetSize() != be_btohl(rpack
->length
)) {
324 dout("Packet size error !!!" << endl
);
326 // TODO : add throw exception
331 CommandsetProcess(command
);
336 ret
= jvmdebug
->WaitStatus(status
);
340 if (printConsoleMessage
!= NULL
)
341 printConsoleMessage(str
);
345 // Read JDB message from host
346 msg
.Receive(command
);
348 if (command
.GetSize() > 0) {
350 rpack
= (const Barry::Protocol::JDWP::Packet
*) command
.GetData();
352 if (command
.GetSize() != be_btohl(rpack
->length
)) {
353 dout("Packet size error !!!" << endl
);
355 // TODO : add throw exception
360 CommandsetProcess(command
);
369 bool JDWServer::Stop()
371 if( handler
.get() ) {
381 if( acceptfd
>= 0 ) {
390 bool JDWServer::InitVisibleClassList()
395 // it's very ugly, but I want use an index started at '1' inside of '0'
396 // JDB works from '1' :(
398 visibleClassList
.push_back(e
);
400 // Count and index the class (start to '1')
402 JDWAppList::iterator it
;
404 for (it
= appList
.begin(); it
!= appList
.end(); it
++) {
405 JDWAppInfo
&appInfo
= it
->second
;
406 JDG::ClassList
&list
= appInfo
.classList
;
408 JDG::ClassList::iterator b
;
410 for (b
= list
.begin(); b
!= list
.end(); b
++) {
412 // I don't from class field, we have to filter the class view by JDB
413 // if ((b->type != 0x824) && (b->type != 0x64)) {
414 if (b
->id
== 0xffffffff) {
422 visibleClassList
.push_back(*b
);
428 visibleClassList
.CreateDefaultEntries();
434 void JDWServer::CommandsetProcess(Data
&cmd
)
436 MAKE_JDWPPACKET(rpack
, cmd
);
438 switch (rpack
->u
.command
.commandset
) {
439 case JDWP_CMDSET_VIRTUALMACHINE
:
440 CommandsetVirtualMachineProcess(cmd
);
443 case JDWP_CMDSET_REFERECENTYPE
:
446 case JDWP_CMDSET_CLASSTYPE
:
449 case JDWP_CMDSET_ARRAYTYPE
:
452 case JDWP_CMDSET_INTERFACETYPE
:
455 case JDWP_CMDSET_METHOD
:
458 case JDWP_CMDSET_FIELD
:
461 case JDWP_CMDSET_OBJECTREFERENCE
:
464 case JDWP_CMDSET_STRINGREFERENCE
:
467 case JDWP_CMDSET_THREADREFERENCE
:
470 case JDWP_CMDSET_THREADGROUPREFERENCE
:
473 case JDWP_CMDSET_ARRAYREFERENCE
:
476 case JDWP_CMDSET_CLASSLOADERREFERENCE
:
479 case JDWP_CMDSET_EVENTREQUEST
:
480 CommandsetEventRequestProcess(cmd
);
483 case JDWP_CMDSET_STACKFRAME
:
486 case JDWP_CMDSET_CLASSOBJECTREFERENCE
:
489 case JDWP_CMDSET_EVENT
:
493 // TODO : add exception (or alert)
494 dout("Commandset unknown !!!" << endl
);
499 void JDWServer::CommandsetVirtualMachineProcess(Data
&cmd
)
501 MAKE_JDWPPACKET(rpack
, cmd
);
503 switch (rpack
->u
.command
.command
) {
504 case JDWP_CMD_VERSION
:
508 case JDWP_CMD_ALLCLASSES
:
509 CommandAllClasses(cmd
);
512 case JDWP_CMD_ALLTHREADS
:
513 CommandAllThreads(cmd
);
516 case JDWP_CMD_DISPOSE
:
518 targetrunning
= false;
523 case JDWP_CMD_IDSIZES
:
527 case JDWP_CMD_SUSPEND
:
529 targetrunning
= false;
532 case JDWP_CMD_RESUME
:
534 targetrunning
= true;
537 case JDWP_CMD_CLASSPATHS
:
538 CommandClassPaths(cmd
);
544 void JDWServer::CommandsetEventRequestProcess(Data
&cmd
)
546 MAKE_JDWPPACKET(rpack
, cmd
);
548 switch (rpack
->u
.command
.command
) {
556 void JDWServer::CommandVersion(Data
&cmd
)
558 JDWMessage
msg(acceptfd
);
563 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
565 AddJDWString(response
, offset
, string("RIM JVM"));
566 AddJDWInt(response
, offset
, be_htobl(1));
567 AddJDWInt(response
, offset
, be_htobl(4));
568 AddJDWString(response
, offset
, string("1.4"));
569 AddJDWString(response
, offset
, string("RIM JVM"));
571 response
.ReleaseBuffer(offset
);
574 size_t total_size
= response
.GetSize();
576 // Fill in the header values
577 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
578 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
581 MAKE_JDWPPACKET(rpack
, cmd
);
583 packet
.length
= be_htobl(total_size
);
584 packet
.id
= rpack
->id
;
586 packet
.u
.response
.errorcode
= be_htobs(0);
588 response
.ReleaseBuffer(total_size
);
593 void JDWServer::CommandAllClasses(Data
&cmd
)
598 JDWMessage
msg(acceptfd
);
603 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
605 // Size of known class list
606 size
= visibleClassList
.size() - 1;
608 AddJDWInt(response
, offset
, be_htobl(size
));
610 // Then, write the list of known class
611 for (i
=1; i
<visibleClassList
.size(); i
++) {
612 string str
= visibleClassList
[i
].GetFullClassName();
614 str
= "L" + str
+ ";";
615 replace_if(str
.begin(), str
.end(), bind2nd(equal_to
<char>(),'.'), '/');
617 AddJDWByte(response
, offset
, 0x01);
618 AddJDWInt(response
, offset
, i
); // Should be equal to visibleClassList[i].index
619 AddJDWString(response
, offset
, str
);
620 AddJDWInt(response
, offset
, be_htobl(0x04));
623 response
.ReleaseBuffer(offset
);
626 size_t total_size
= response
.GetSize();
628 // Fill in the header values
629 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
630 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
633 MAKE_JDWPPACKET(rpack
, cmd
);
635 packet
.length
= be_htobl(total_size
);
636 packet
.id
= rpack
->id
;
638 packet
.u
.response
.errorcode
= be_htobs(0);
640 response
.ReleaseBuffer(total_size
);
645 void JDWServer::CommandAllThreads(Data
&cmd
)
647 JDWMessage
msg(acceptfd
);
649 // Get threads list from device
651 jvmdebug
->GetThreadsList(list
);
657 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
659 // Indicate the number of element
660 AddJDWInt(response
, offset
, be_htobl(list
.size()));
662 // Send all threads ID
663 JVMThreadsList::const_iterator b
= list
.begin();
664 for( ; b
!= list
.end(); b
++ ) {
665 const JVMThreadsEntry
&entry
= *b
;
667 AddJDWInt(response
, offset
, be_htobl(entry
.Id
));
670 response
.ReleaseBuffer(offset
);
673 size_t total_size
= response
.GetSize();
675 // Fill in the header values
676 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
677 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
680 MAKE_JDWPPACKET(rpack
, cmd
);
682 packet
.length
= be_htobl(total_size
);
683 packet
.id
= rpack
->id
;
685 packet
.u
.response
.errorcode
= be_htobs(0);
687 response
.ReleaseBuffer(total_size
);
692 void JDWServer::CommandIdSizes(Data
&cmd
)
694 JDWMessage
msg(acceptfd
);
696 MAKE_JDWPPACKET(rpack
, cmd
);
700 Barry::Protocol::JDWP::Packet packet
;
702 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
703 + JDWP_PACKETVIRTUALMACHINEIDSIZES_DATA_SIZE
;
705 packet
.length
= be_htobl(size
);
706 packet
.id
= rpack
->id
;
708 packet
.u
.response
.errorcode
= be_htobs(0);
709 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.fieldIDSize
= be_htobl(0x04);
710 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.methodIDSize
= be_htobl(0x04);
711 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.objectIDSize
= be_htobl(0x04);
712 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.referenceTypeIDSize
= be_htobl(0x04);
713 packet
.u
.response
.u
.virtualMachine
.u
.IDSizes
.frameIDSize
= be_htobl(0x04);
715 Data
response(&packet
, size
);
721 void JDWServer::CommandSuspend(Data
&cmd
)
723 JDWMessage
msg(acceptfd
);
730 MAKE_JDWPPACKET(rpack
, cmd
);
734 Barry::Protocol::JDWP::Packet packet
;
736 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
738 packet
.length
= be_htobl(size
);
739 packet
.id
= rpack
->id
;
741 packet
.u
.response
.errorcode
= be_htobs(0);
743 Data
response(&packet
, size
);
749 void JDWServer::CommandResume(Data
&cmd
)
751 JDWMessage
msg(acceptfd
);
755 jvmdebug
->Unknown06();
756 jvmdebug
->Unknown07();
757 jvmdebug
->Unknown08();
758 jvmdebug
->Unknown09();
759 jvmdebug
->Unknown10();
763 MAKE_JDWPPACKET(rpack
, cmd
);
767 Barry::Protocol::JDWP::Packet packet
;
769 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
771 packet
.length
= be_htobl(size
);
772 packet
.id
= rpack
->id
;
774 packet
.u
.response
.errorcode
= be_htobs(0);
776 Data
response(&packet
, size
);
782 void JDWServer::CommandClassPaths(Data
&cmd
)
784 JDWMessage
msg(acceptfd
);
789 size_t offset
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
;
791 AddJDWString(response
, offset
, string(""));
792 AddJDWInt(response
, offset
, be_htobl(0));
793 AddJDWInt(response
, offset
, be_htobl(0));
795 response
.ReleaseBuffer(offset
);
798 size_t total_size
= response
.GetSize();
800 // Fill in the header values
801 MAKE_JDWPPACKETPTR_BUF(cpack
, response
.GetBuffer(total_size
));
802 Barry::Protocol::JDWP::Packet
&packet
= *cpack
;
805 MAKE_JDWPPACKET(rpack
, cmd
);
807 packet
.length
= be_htobl(total_size
);
808 packet
.id
= rpack
->id
;
810 packet
.u
.response
.errorcode
= be_htobs(0);
812 response
.ReleaseBuffer(total_size
);
818 void JDWServer::CommandSet(Data
&cmd
)
820 static int value
= 2;
822 JDWMessage
msg(acceptfd
);
824 MAKE_JDWPPACKET(rpack
, cmd
);
828 Barry::Protocol::JDWP::Packet packet
;
830 size
= JDWP_PACKET_HEADER_SIZE
+ JDWP_RESPONSE_HEADER_SIZE
+ sizeof(uint32_t);
832 packet
.length
= be_htobl(size
);
833 packet
.id
= rpack
->id
;
835 packet
.u
.response
.errorcode
= be_htobs(0);
836 packet
.u
.response
.u
.value
= be_htobl(value
);
838 Data
response(&packet
, size
);
846 }} // namespace Barry::JDWP