1 /*
2  * Copyright (c) 2001, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package nsk.share.jdwp;
25 
26 import nsk.share.*;
27 
28 import java.util.Vector;
29 import java.io.*;
30 
31 /**
32  * This class represents a JDWP packet.
33  */
34 public class Packet extends ByteBuffer {
35 
36     /** JDWP packet flags constant. */
37 //    public final static byte flNoFlags          = (byte)0x0;
38     /** JDWP packet flags constant. */
39 //    public final static byte flReply            = (byte)0x80;
40 
41     /** Offset of "length" field of JDWP packet. */
42     public final static int LengthOffset        = 0;
43     /** Offset of "id" field of JDWP packet. */
44     public final static int IdOffset            = LengthOffset + 4;
45     /** Offset of "flags" field of JDWP packet. */
46     public final static int FlagsOffset         = IdOffset + 4;
47     /** Offset of full "command" field of JDWP packet. */
48     public final static int FullCommandOffset   = FlagsOffset + 1;
49     /** Offset of "command" field of JDWP command packet. */
50     public final static int CommandSetOffset    = FullCommandOffset;
51     /** Offset of "command" field of JDWP command packet. */
52     public final static int CommandOffset       = CommandSetOffset + 1;
53     /** Offset of "error" field of JDWP reply packet. */
54     public final static int ErrorCodeOffset     = FlagsOffset + 1;
55     /** Offset of "data" section of JDWP packet. */
56     public final static int DataOffset          = FullCommandOffset + 2;
57 
58     /** Size of JDWP packet header. */
59     public final static int PacketHeaderSize    = DataOffset;
60 
61     /**
62      * Makes empty JDWP packet.
63      */
Packet()64     public Packet() {
65         super();
66         resetBuffer();
67     }
68 
69     /**
70      * Makes JDWP packet with data from the specified byte buffer.
71      */
72 //    public Packet(ByteBuffer packet) {
Packet(Packet packet)73     public Packet(Packet packet) {
74         super(packet);
75         resetPosition();
76     }
77 
78     /**
79      * Clear buffer of the packet.
80      */
resetBuffer()81     public void resetBuffer() {
82         super.resetBuffer();
83         while (length() < PacketHeaderSize)
84             addByte((byte) 0);
85         setLength();
86         resetPosition();
87     }
88 
89     /**
90      * Return current position from begin of packet data area.
91      */
currentDataPosition()92     public int currentDataPosition() {
93         return currentPosition() - PacketHeaderSize;
94     }
95 
96     /**
97      * Return value to the "length" field of JDWP packet.
98      */
getLength()99     public int getLength() {
100         try {
101             return getInt(LengthOffset);
102         }
103         catch (BoundException e) {
104             throw new Failure("Caught unexpected exception while getting packet length value from header:\n\t"
105                             + e);
106         }
107     }
108 
109     /**
110      * Assign specified value to the "length" field of JDWP packet.
111      */
setLength(int length)112     public void setLength(int length) {
113         try {
114             putInt(LengthOffset, length);
115         }
116         catch (BoundException e) {
117             throw new Failure("Caught unexpected exception while setting packet length value into header:\n\t"
118                             + e);
119         }
120     }
121 
122     /**
123      * Assign packet length value to the "length" field of JDWP packet.
124      */
setLength()125     public void setLength() {
126         setLength(length());
127     }
128 
129     /**
130      * Return value of the "id" field of JDWP packet.
131      */
getPacketID()132     public int getPacketID() {
133         try {
134             return getInt(IdOffset);
135         }
136         catch (BoundException e) {
137             throw new Failure("Caught unexpected exception while getting packet ID value from header:\n\t"
138                             + e);
139         }
140     }
141 
142     /**
143      * Assign value to the "id" field of JDWP packet.
144      */
setPacketID(int Id)145     public void setPacketID(int Id) {
146         try {
147             putInt(IdOffset, Id);
148         }
149         catch (BoundException e) {
150             throw new Failure("Caught unexpected exception while setting packet ID value into header:\n\t"
151                             + e);
152         }
153     }
154 
155     /**
156      * Return value of the "flags" field of JDWP packet.
157      */
getFlags()158     public byte getFlags() {
159         try {
160             return getByte(FlagsOffset);
161         }
162         catch (BoundException e) {
163             throw new Failure("Caught unexpected exception while getting packet flags value from header:\n\t"
164                             + e);
165         }
166     }
167 
168     /**
169      * Assign value to the "flags" field of JDWP packet.
170      */
setFlags(byte flags)171     public void setFlags(byte flags) {
172         try {
173             putByte(FlagsOffset, flags);
174         }
175         catch (BoundException e) {
176             throw new Failure("Caught unexpected exception while setting packet flags value into header:\n\t"
177                             + e);
178         }
179     }
180 
181     /**
182      * Sets the current parser position to "data" field of JDWP packet.
183      */
resetPosition()184     public void resetPosition() {
185         resetPosition(PacketHeaderSize);
186     }
187 
188     /**
189      * Return size of the "data" part of JDWP packet.
190      */
getDataSize()191     public int getDataSize() {
192         return length() - PacketHeaderSize;
193     }
194 
195     //////////////////////////////////////////////////////////////////////////
196 
197     /**
198      * Append fieldID to the end of this buffer.
199      */
addFieldID(long b)200     public void addFieldID(long b) {
201         addID(b, JDWP.TypeSize.FIELD_ID);
202     }
203 
204     /**
205      * Append methodID to the end of this buffer.
206      */
addMethodID(long b)207     public void addMethodID(long b) {
208         addID(b, JDWP.TypeSize.METHOD_ID);
209     }
210 
211     /**
212      * Append objectID to the end of this buffer.
213      */
addObjectID(long b)214     public void addObjectID(long b) {
215         addID(b, JDWP.TypeSize.OBJECT_ID);
216     }
217 
218     /**
219      * Append referenceID to the end of this buffer.
220      */
addReferenceTypeID(long b)221     public void addReferenceTypeID(long b) {
222         addID(b, JDWP.TypeSize.REFERENCE_TYPE_ID);
223     }
224 
225     /**
226      * Append frameID to the end of this buffer.
227      */
addFrameID(long b)228     public void addFrameID(long b) {
229         addID(b, JDWP.TypeSize.FRAME_ID);
230     }
231 
232     /**
233      * Append string value (an UTF-8 encoded string, not zero terminated,
234      * preceded by a four-byte integer length) to the end of this buffer.
235      */
addString(String value)236     public void addString(String value) {
237         final int count = JDWP.TypeSize.INT + value.length();
238         addInt(value.length());
239         try {
240             addBytes(value.getBytes("UTF-8"), 0, value.length());
241         } catch (UnsupportedEncodingException e) {
242             throw new Failure("Unsupported UTF-8 ecnoding while adding string value to JDWP packet:\n\t"
243                                 + e);
244         }
245     }
246 
247     /**
248      * Append location value to the end of this buffer.
249      */
addLocation(JDWP.Location location)250     public void addLocation(JDWP.Location location) {
251         addBytes(location.getBytes(), 0, location.length());
252     }
253 
254     /**
255      * Append untagged value to the end of this buffer.
256      */
addUntaggedValue(JDWP.UntaggedValue value, byte tag)257     public void addUntaggedValue(JDWP.UntaggedValue value, byte tag) {
258         value.addValueTo(this, tag);
259     }
260 
261     /**
262      * Append tagged value to the end of this buffer.
263      */
addValue(JDWP.Value value)264     public void addValue(JDWP.Value value) {
265         value.addValueTo(this);
266     }
267 
268     //////////////////////////////////////////////////////////////////////////
269     // get packet data
270     //////////////////////////////////////////////////////////////////////////
271 
272     /**
273      * Read a fieldID value from byte buffer at the current parser position.
274      *
275      * @throws BoundException if there is no valid value bytes at the given position
276      */
getFieldID()277     public long getFieldID() throws BoundException {
278         return getID(JDWP.TypeSize.FIELD_ID);
279     }
280 
281     /**
282      * Read a methodID value from byte buffer at the current parser position.
283      *
284      * @throws BoundException if there is no valid value bytes at the given position
285      */
getMethodID()286     public long getMethodID() throws BoundException {
287         return getID(JDWP.TypeSize.METHOD_ID);
288     }
289 
290     /**
291      * Read an objectID value from byte buffer at the current parser position.
292      *
293      * @throws BoundException if there is no valid value bytes at the given position
294      */
getObjectID()295     public long getObjectID() throws BoundException {
296         return getID(JDWP.TypeSize.OBJECT_ID);
297     }
298 
299     /**
300      * Read a referenceTypeID value from byte buffer at the current parser position.
301      *
302      * @throws BoundException if there is no valid value bytes at the given position
303      */
getReferenceTypeID()304     public long getReferenceTypeID() throws BoundException {
305         return getID(JDWP.TypeSize.REFERENCE_TYPE_ID);
306     }
307 
308     /**
309      * Read a frameID value from byte buffer at the current parser position.
310      *
311      * @throws BoundException if there is no valid value bytes at the given position
312      */
getFrameID()313     public long getFrameID() throws BoundException {
314         return getID(JDWP.TypeSize.FRAME_ID);
315     }
316 
317     /**
318      * Read from this buffer a string value at the current parser
319      * position and returns this value.
320      *
321      * @throws BoundException if there are no valid string bytes in the buffer
322      */
getString()323     public String getString() throws BoundException {
324         final int count = JDWP.TypeSize.INT;
325         int available = length() - currentPosition();
326         if (count > available) {
327             throw new BoundException("Unable to get " + count + " bytes of string length value at " +
328                                       offsetString() + " (available bytes: " + available + ")" );
329         }
330 
331         int len = getInt();
332 
333         if (len < 0)
334             throw new BoundException("Negative length of string to get: " + len);
335 
336         if (len == 0)
337             return "";
338 
339         available = length() - currentPosition();
340         if (len > available) {
341             throw new BoundException("Unable to get " + len + " bytes of string value at " +
342                                      offsetString() + " (available bytes: " + available + ")" );
343         }
344 
345         byte[] s = new byte[len];
346         for (int i = 0; i < len; i++)
347             s[i] = getByte();
348 
349         try {
350             return new String(s, "UTF-8");
351         } catch (UnsupportedEncodingException e) {
352             throw new Failure("Unsupported UTF-8 ecnoding while extracting string value from JDWP packet:\n\t"
353                                 + e);
354         }
355     }
356 
357     /**
358      * Reads from this buffer an location value at the current parser
359      * position and returns this value.
360      *
361      * @throws BoundException if there are no enough bytes in the buffer
362      */
getLocation()363     public JDWP.Location getLocation() throws BoundException {
364         final int count = JDWP.TypeSize.LOCATION;
365         final int available = length() - currentPosition();
366         if (count > available) {
367             throw new BoundException("Unable to get " + count + " bytes of location value at " +
368                                      offsetString() + " (available bytes: " + available + ")" );
369         }
370 
371         JDWP.Location location = new JDWP.Location();
372         try {
373             for (int i = 0; i < JDWP.TypeSize.LOCATION; i++) {
374                 location.putByte(i, getByte());
375             }
376         }
377         catch (BoundException e) {
378             throw new TestBug("Caught unexpected bound exception while getting " +
379                                count + " bytes of location value:\n\t" + e);
380         };
381 
382         return location;
383     }
384 
385     /**
386      * Reads from this buffer an untagged value at the current parser
387      * position and returns this value.
388      *
389      * @throws BoundException if there are no enough bytes in the buffer
390      */
getUntaggedValue(byte tag)391     public JDWP.UntaggedValue getUntaggedValue(byte tag) throws BoundException {
392         JDWP.UntaggedValue value = new JDWP.UntaggedValue();
393         try {
394             value.getValueFrom(this, tag);
395         }
396         catch (BoundException e) {
397             throw new TestBug("Caught unexpected bound exception while getting " +
398                                " bytes of a value:\n\t" + e);
399         };
400 
401         return value;
402     }
403 
404     /**
405      * Reads from this buffer a tagged value at the current parser
406      * position and returns this value.
407      *
408      * @throws BoundException if there are no enough bytes in the buffer
409      */
getValue()410     public JDWP.Value getValue() throws BoundException {
411         JDWP.Value value = new JDWP.Value();
412         try {
413             value.getValueFrom(this);
414         }
415         catch (BoundException e) {
416             throw new TestBug("Caught unexpected bound exception while getting " +
417                                " bytes of a value:\n\t" + e);
418         };
419 
420         return value;
421     }
422 
423 
424     ////////////////////////////////////////////////////////////////
425 
426     /**
427      * Read packet bytes from the stream.
428      *
429      * @throws IOException if error occured when reading bytes
430      */
readFrom(Transport transport)431     public void readFrom(Transport transport) throws IOException {
432         resetBuffer();
433 
434 //        System.err.println("Reading packet header");
435         try {
436             for (int i = 0; i < PacketHeaderSize; i++) {
437                 byte b = transport.read();
438                 putByte(i, b);
439             }
440         }
441         catch (BoundException e) {
442             throw new TestBug(e);
443         }
444 
445         int length = getLength();
446 
447         checkSpace(length - PacketHeaderSize);
448 
449 //        System.err.println("Packet size: " + length);
450         for (int i = PacketHeaderSize; i < length; i++) {
451             byte b = transport.read();
452             addByte(b);
453         }
454 //        System.err.println("Packet read successfully");
455     }
456 
457     /**
458      * Write packet bytes to the stream.
459      *
460      * @throws IOException if error occured when reading bytes
461      */
writeTo(Transport transport)462     public void writeTo(Transport transport) throws IOException {
463         setLength();
464 //        System.err.println("Writing packet bytes: " + length());
465         transport.write(bytes, 0, length());
466     }
467 
468     /**
469      * Check packet header.
470      * This method check if packet has valid values in header fields.
471      *
472      * @throws PacketFormatException if packet header fields has error or invalid values
473      */
checkHeader()474     public void checkHeader() throws PacketFormatException {
475         if (getLength() != length()) {
476             throw new PacketFormatException("Unexpected packet length value in the header:"
477                                             + getLength());
478         }
479     }
480 
481     /**
482      * Check if packet is parsed totally and no trailing bytes left unparsed.
483      * This method check if packet has valid values in header fields.
484      *
485      * @throws PacketFormatException if there are trailing bytes left unparsed
486      */
checkParsed()487     public void checkParsed() throws PacketFormatException {
488         if (! isParsed()) {
489             throw new PacketFormatException("Extra trailing bytes found in the packet at: "
490                                             + offsetString());
491         }
492     }
493 
494     /**
495      * Return string representation of the packet header.
496      */
headerToString()497     public String headerToString() {
498         return "Packet header (" + PacketHeaderSize + " bytes):" + "\n"
499              + "    " + toHexString(LengthOffset, 4) + " (length) : 0x" + toHexDecString(getLength(), 8) + "\n"
500              + "    " + toHexString(IdOffset,     4) + " (id)     : 0x" + toHexDecString(getPacketID(), 8) + "\n"
501              + "    " + toHexString(FlagsOffset,  4) + " (flags)  : 0x" + toHexDecString(getFlags(), 2) + "\n";
502     }
503 
504     /**
505      * Return string representation of the packet.
506      */
toString()507     public String toString() {
508         return headerToString()
509              + "Entire packet (" + length() + " bytes): " + "\n"
510              + super.toString(0)
511              + "Packet end";
512     }
513 
514     /**
515      * Exception indicated that packet has an ivnalid structure.
516      */
517     class PacketFormatException extends BoundException {
PacketFormatException(String message)518         PacketFormatException(String message) {
519             super(message);
520         }
521     }
522 }
523