1 /*
2  * Copyright (c) 2003, 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.jdi.VirtualMachineManager.createVirtualMachine;
25 
26 import com.sun.jdi.*;
27 import com.sun.jdi.connect.*;
28 import com.sun.jdi.connect.spi.*;
29 import java.net.*;
30 import java.io.*;
31 import java.util.*;
32 
33 /*
34  * A transport service implementation based on a TCP connection used by
35  * nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003 test.
36  * It is borrowed from the com.sun.tools.jdi.SocketTransportService
37  */
38 
39 public class CreateVM003_TranspServ extends TransportService {
40 
41     /**
42      * The listener returned by startListening encapsulates
43      * the ServerSocket.
44      */
45     static class SocketListenKey extends ListenKey {
46         ServerSocket ss;
47 
SocketListenKey(ServerSocket ss)48         SocketListenKey(ServerSocket ss) {
49             this.ss = ss;
50         }
51 
socket()52         ServerSocket socket() {
53             return ss;
54         }
55 
address()56         public String address() {
57             InetAddress localaddr = ss.getInetAddress();
58             int port = ss.getLocalPort();
59             if (localaddr.isAnyLocalAddress()) {
60                 return "" + port;
61             } else {
62                 return localaddr + ":" + port;
63             }
64         }
65 
toString()66         public String toString() {
67             return address();
68         }
69     }
70 
71     /**
72      * Handshake with the debuggee
73      */
handshake(Socket s, long timeout)74     void handshake(Socket s, long timeout) throws IOException {
75         s.setSoTimeout((int)timeout);
76 
77         byte[] hello = "JDWP-Handshake".getBytes("UTF-8");
78         s.getOutputStream().write(hello);
79 
80         byte[] b = new byte[hello.length];
81         int received = 0;
82         while (received < hello.length) {
83             int n;
84             try {
85                 n = s.getInputStream().read(b, received, hello.length-received);
86             } catch (SocketTimeoutException x) {
87                 throw new IOException("##> CreateVM003_TranspServ: Handshake timeout");
88             }
89             if (n < 0) {
90                 s.close();
91                 throw new IOException
92                     ("##> CreateVM003_TranspServ: Handshake FAILED - connection prematurally closed!");
93             }
94             received += n;
95         }
96         for (int i=0; i<hello.length; i++) {
97             if (b[i] != hello[i]) {
98                 throw new IOException
99                     ("##> CreateVM003_TranspServ: Handshake FAILED - unrecognized message from target VM");
100             }
101         }
102 
103         // disable read timeout
104         s.setSoTimeout(0);
105     }
106 
107     /**
108      * No-arg constructor
109      */
CreateVM003_TranspServ()110     public CreateVM003_TranspServ() {
111     }
112 
113     /**
114      * The name of this transport service
115      */
name()116     public String name() {
117         return "CreateVM003_TranspServ";
118     }
119 
120     /**
121      * The description of this transport service
122      */
description()123     public String description() {
124         return "SocketTransportService for nsk/jdi/VirtualMachineManager/createVirtualMachine/createVM003 test.";
125     }
126 
127     /**
128      * Return the capabilities of this transport service
129      */
capabilities()130     public Capabilities capabilities() {
131         return new CreateVM003_TranspServCapabilities();
132     }
133 
134 
135     /**
136      * Attach to the specified address with optional attach and handshake
137      * timeout.
138      */
attach(String address, long attachTimeout, long handshakeTimeout)139     public Connection attach(String address, long attachTimeout, long handshakeTimeout)
140         throws IOException {
141 
142         if (address == null) {
143             throw new NullPointerException("##> CreateVM003_TranspServ: attach() - address is null");
144         }
145         if (attachTimeout < 0 || handshakeTimeout < 0) {
146             throw new IllegalArgumentException("##> CreateVM003_TranspServ: attach() - timeout is negative");
147         }
148 
149         int splitIndex = address.indexOf(':');
150         String host;
151         String portStr;
152         if (splitIndex < 0) {
153             host = InetAddress.getLocalHost().getHostName();
154             portStr = address;
155         } else {
156             host = address.substring(0, splitIndex);
157             portStr = address.substring(splitIndex+1);
158         }
159 
160         int port;
161         try {
162             port = Integer.decode(portStr).intValue();
163         } catch (NumberFormatException e) {
164             throw new IllegalArgumentException(
165                 "##> CreateVM003_TranspServ: attach() - unable to parse port number in address");
166         }
167 
168 
169         // open TCP connection to VM
170 
171         InetSocketAddress sa = new InetSocketAddress(host, port);
172         Socket s = new Socket();
173         try {
174             s.connect(sa, (int)attachTimeout);
175         } catch (SocketTimeoutException exc) {
176             try {
177                 s.close();
178             } catch (IOException x) { }
179             throw new TransportTimeoutException
180                 ("##> CreateVM003_TranspServ: attach() - timed out trying to establish connection");
181         }
182 
183         // handshake with the target VM
184         try {
185             handshake(s, handshakeTimeout);
186         } catch (IOException exc) {
187             try {
188                 s.close();
189             } catch (IOException x) { }
190             throw exc;
191         }
192 
193         return new CreateVM003_Connection(s);
194     }
195 
196     /*
197      * Listen on the specified address and port. Return a listener
198      * that encapsulates the ServerSocket.
199      */
startListening(String localaddress, int port)200     ListenKey startListening(String localaddress, int port) throws IOException {
201         InetSocketAddress sa;
202         if (localaddress == null) {
203             sa = new InetSocketAddress(port);
204         } else {
205             sa = new InetSocketAddress(localaddress, port);
206         }
207         ServerSocket ss = new ServerSocket();
208         // if port is 0 do not set the SO_REUSEADDR flag
209         if (port == 0) {
210             ss.setReuseAddress(false);
211         }
212         ss.bind(sa);
213         return new SocketListenKey(ss);
214     }
215 
216     /**
217      * Listen on the specified address
218      */
startListening(String address)219     public ListenKey startListening(String address) throws IOException {
220         // use ephemeral port if address isn't specified.
221         if (address == null || address.length() == 0) {
222             address = "0";
223         }
224 
225         int splitIndex = address.indexOf(':');
226         String localaddr = null;
227         if (splitIndex >= 0) {
228             localaddr = address.substring(0, splitIndex);
229             address = address.substring(splitIndex+1);
230         }
231 
232         int port;
233         try {
234             port = Integer.decode(address).intValue();
235         } catch (NumberFormatException e) {
236             throw new IllegalArgumentException(
237                     "##> CreateVM003_TranspServ: startListening() - unable to parse port number in address");
238         }
239 
240         return startListening(localaddr, port);
241     }
242 
243     /**
244      * Listen on the default address
245      */
startListening()246     public ListenKey startListening() throws IOException {
247         return startListening(null, 0);
248     }
249 
250     /**
251      * Stop the listener
252      */
stopListening(ListenKey listener)253     public void stopListening(ListenKey listener) throws IOException {
254         if (!(listener instanceof SocketListenKey)) {
255             throw new IllegalArgumentException
256                 ("##> CreateVM003_TranspServ: stopListening() - Invalid listener");
257         }
258 
259         synchronized (listener) {
260             ServerSocket ss = ((SocketListenKey)listener).socket();
261 
262             // if the ServerSocket has been closed it means
263             // the listener is invalid
264             if (ss.isClosed()) {
265                 throw new IllegalArgumentException
266                     ("##> CreateVM003_TranspServ: stopListening() - Invalid listener");
267             }
268             ss.close();
269         }
270     }
271 
272     /**
273      * Accept a connection from a debuggee and handshake with it.
274      */
accept(ListenKey listener, long acceptTimeout, long handshakeTimeout)275     public Connection accept(ListenKey listener, long acceptTimeout, long handshakeTimeout) throws IOException {
276         if (acceptTimeout < 0 || handshakeTimeout < 0) {
277             throw new IllegalArgumentException
278                 ("##> CreateVM003_TranspServ: accept() - timeout is negative");
279         }
280         if (!(listener instanceof SocketListenKey)) {
281             throw new IllegalArgumentException
282                 ("##> CreateVM003_TranspServ: accept() - Invalid listener");
283         }
284         ServerSocket ss;
285 
286         // obtain the ServerSocket from the listener - if the
287         // socket is closed it means the listener is invalid
288         synchronized (listener) {
289             ss = ((SocketListenKey)listener).socket();
290             if (ss.isClosed()) {
291                throw new IllegalArgumentException
292                    ("##> CreateVM003_TranspServ: accept() - Invalid listener");
293             }
294         }
295 
296         ss.setSoTimeout((int)acceptTimeout);
297         Socket s;
298         try {
299             s = ss.accept();
300         } catch (SocketTimeoutException x) {
301             throw new TransportTimeoutException
302                 ("##> CreateVM003_TranspServ: accept() - timeout waiting for connection");
303         }
304 
305         // handshake here
306         handshake(s, handshakeTimeout);
307 
308         return new CreateVM003_Connection(s);
309     }
310 
toString()311     public String toString() {
312        return name();
313     }
314 } // end of CreateVM003_TranspServ class
315 
316 class CreateVM003_Connection extends Connection {
317     private Socket socket;
318     private boolean closed = false;
319     private OutputStream socketOutput;
320     private InputStream socketInput;
321     private Object receiveLock = new Object();
322     private Object sendLock = new Object();
323     private Object closeLock = new Object();
324 
CreateVM003_Connection(Socket socket)325     CreateVM003_Connection(Socket socket) throws IOException {
326         this.socket = socket;
327         socket.setTcpNoDelay(true);
328         socketInput = socket.getInputStream();
329         socketOutput = socket.getOutputStream();
330     }
331 
close()332     public void close() throws IOException {
333         synchronized (closeLock) {
334            if (closed) {
335                 return;
336            }
337            socketOutput.close();
338            socketInput.close();
339            socket.close();
340            closed = true;
341         }
342     }
343 
isOpen()344     public boolean isOpen() {
345         synchronized (closeLock) {
346             return !closed;
347         }
348     }
349 
readPacket()350     public byte[] readPacket() throws IOException {
351         if (!isOpen()) {
352             throw new ClosedConnectionException
353                 ("##> CreateVM003_Connection: readPacket() - connection is closed");
354         }
355         synchronized (receiveLock) {
356             int b1,b2,b3,b4;
357 
358             // length
359             try {
360                 b1 = socketInput.read();
361                 b2 = socketInput.read();
362                 b3 = socketInput.read();
363                 b4 = socketInput.read();
364             } catch (IOException ioe) {
365                 if (!isOpen()) {
366                     throw new ClosedConnectionException
367                         ("##> CreateVM003_Connection: readPacket() - connection is closed");
368                 } else {
369                     throw ioe;
370                 }
371             }
372 
373             if (b1<0 || b2<0 || b3<0 || b4<0)
374                 throw new EOFException();
375 
376             int len = ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0));
377 
378             if (len < 0) {
379                 throw new IOException
380                     ("##> CreateVM003_Connection: readPacket() - protocol error: invalid Packet's length");
381             }
382 
383             byte b[] = new byte[len];
384             b[0] = (byte)b1;
385             b[1] = (byte)b2;
386             b[2] = (byte)b3;
387             b[3] = (byte)b4;
388 
389             int off = 4;
390             len -= off;
391 
392             while (len > 0) {
393                 int count;
394                 try {
395                     count = socketInput.read(b, off, len);
396                 } catch (IOException ioe) {
397                     if (!isOpen()) {
398                         throw new ClosedConnectionException
399                             ("##> CreateVM003_Connection: readPacket() - connection is closed");
400                     } else {
401                         throw ioe;
402                     }
403                 }
404                 if (count < 0) {
405                     throw new EOFException
406                         ("##> CreateVM003_Connection: readPacket() - read() method returns negative value");
407                 }
408                 len -= count;
409                 off += count;
410             }
411 
412             return b;
413         }
414     }
415 
writePacket(byte b[])416     public void writePacket(byte b[]) throws IOException {
417         if (!isOpen()) {
418             throw new ClosedConnectionException
419                 ("##> CreateVM003_Connection: writePacket() - connection is closed");
420         }
421 
422         /*
423          * Check the packet size
424          */
425         if (b.length < 11) {
426             throw new IllegalArgumentException
427                 ("##> CreateVM003_Connection: writePacket() - packet is insufficient size");
428         }
429         int b0 = b[0] & 0xff;
430         int b1 = b[1] & 0xff;
431         int b2 = b[2] & 0xff;
432         int b3 = b[3] & 0xff;
433         int len = ((b0 << 24) | (b1 << 16) | (b2 << 8) | (b3 << 0));
434         if (len < 11) {
435             throw new IllegalArgumentException
436                 ("##> CreateVM003_Connection: writePacket() - packet is insufficient size");
437         }
438 
439         /*
440          * Check that the byte array contains the complete packet
441          */
442         if (len > b.length) {
443             throw new IllegalArgumentException
444                 ("##> CreateVM003_Connection: writePacket() - length mis-match");
445         }
446 
447         synchronized (sendLock) {
448             try {
449                 /*
450                  * Send the packet (ignoring any bytes that follow
451                  * the packet in the byte array).
452                  */
453                 socketOutput.write(b, 0, len);
454             } catch (IOException ioe) {
455                 if (!isOpen()) {
456                     throw new ClosedConnectionException
457                         ("##> CreateVM003_Connection: writePacket() - connection is closed");
458                 } else {
459                     throw ioe;
460                 }
461             }
462         }
463     }
464 
465 } // end of CreateVM003_Connection class
466 
467 /*
468  * The capabilities of the socket transport service
469  */
470 class CreateVM003_TranspServCapabilities extends TransportService.Capabilities {
471 
supportsMultipleConnections()472     public boolean supportsMultipleConnections() {
473         return true;
474     }
475 
supportsAttachTimeout()476     public boolean supportsAttachTimeout() {
477         return true;
478     }
479 
supportsAcceptTimeout()480     public boolean supportsAcceptTimeout() {
481         return true;
482     }
483 
supportsHandshakeTimeout()484     public boolean supportsHandshakeTimeout() {
485         return true;
486     }
487 
488 } // end of CreateVM003_TranspServCapabilities class
489