• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1998, 2003, 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 package com.sun.tools.jdi;
27 
28 import com.sun.jdi.*;
29 import com.sun.jdi.connect.*;
30 import com.sun.jdi.connect.spi.*;
31 import java.net.*;
32 import java.io.*;
33 import java.util.Map;
34 import java.util.ResourceBundle;
35 
36 /*
37  * A transport service based on a TCP connection between the
38  * debugger and debugee.
39  */
40 
41 public class SocketTransportService extends TransportService {
42     private ResourceBundle messages = null;
43 
44     /**
45      * The listener returned by startListening encapsulates
46      * the ServerSocket.
47      */
48     static class SocketListenKey extends ListenKey {
49         ServerSocket ss;
50 
SocketListenKey(ServerSocket ss)51         SocketListenKey(ServerSocket ss) {
52             this.ss = ss;
53         }
54 
socket()55         ServerSocket socket() {
56             return ss;
57         }
58 
59         /*
60          * Returns the string representation of the address that this
61          * listen key represents.
62          */
address()63         public String address() {
64             InetAddress address = ss.getInetAddress();
65 
66             /*
67              * If bound to the wildcard address then use current local
68              * hostname. In the event that we don't know our own hostname
69              * then assume that host supports IPv4 and return something to
70              * represent the loopback address.
71              */
72             if (address.isAnyLocalAddress()) {
73                 try {
74                     address = InetAddress.getLocalHost();
75                 } catch (UnknownHostException uhe) {
76                     byte[] loopback = {0x7f,0x00,0x00,0x01};
77                     try {
78                         address = InetAddress.getByAddress("127.0.0.1", loopback);
79                     } catch (UnknownHostException x) {
80                         throw new InternalError("unable to get local hostname");
81                     }
82                 }
83             }
84 
85             /*
86              * Now decide if we return a hostname or IP address. Where possible
87              * return a hostname but in the case that we are bound to an
88              * address that isn't registered in the name service then we
89              * return an address.
90              */
91             String result;
92             String hostname = address.getHostName();
93             String hostaddr = address.getHostAddress();
94             if (hostname.equals(hostaddr)) {
95                 if (address instanceof Inet6Address) {
96                     result = "[" + hostaddr + "]";
97                 } else {
98                     result = hostaddr;
99                 }
100             } else {
101                 result = hostname;
102             }
103 
104             /*
105              * Finally return "hostname:port", "ipv4-address:port" or
106              * "[ipv6-address]:port".
107              */
108             return result + ":" + ss.getLocalPort();
109         }
110 
toString()111         public String toString() {
112             return address();
113         }
114     }
115 
116     /**
117      * Handshake with the debuggee
118      */
handshake(Socket s, long timeout)119     void handshake(Socket s, long timeout) throws IOException {
120         s.setSoTimeout((int)timeout);
121 
122         byte[] hello = "JDWP-Handshake".getBytes("UTF-8");
123         s.getOutputStream().write(hello);
124 
125         byte[] b = new byte[hello.length];
126         int received = 0;
127         while (received < hello.length) {
128             int n;
129             try {
130                 n = s.getInputStream().read(b, received, hello.length-received);
131             } catch (SocketTimeoutException x) {
132                 throw new IOException("handshake timeout");
133             }
134             if (n < 0) {
135                 s.close();
136                 throw new IOException("handshake failed - connection prematurally closed");
137             }
138             received += n;
139         }
140         for (int i=0; i<hello.length; i++) {
141             if (b[i] != hello[i]) {
142                 throw new IOException("handshake failed - unrecognized message from target VM");
143             }
144         }
145 
146         // disable read timeout
147         s.setSoTimeout(0);
148     }
149 
150     /**
151      * No-arg constructor
152      */
SocketTransportService()153     public SocketTransportService() {
154     }
155 
156     /**
157      * The name of this transport service
158      */
name()159     public String name() {
160         return "Socket";
161     }
162 
163     /**
164      * Return localized description of this transport service
165      */
description()166     public String description() {
167         synchronized (this) {
168             if (messages == null) {
169                 messages = ResourceBundle.getBundle("com.sun.tools.jdi.resources.jdi");
170             }
171         }
172         return messages.getString("socket_transportservice.description");
173     }
174 
175     /**
176      * Return the capabilities of this transport service
177      */
capabilities()178     public Capabilities capabilities() {
179         return new SocketTransportServiceCapabilities();
180     }
181 
182 
183     /**
184      * Attach to the specified address with optional attach and handshake
185      * timeout.
186      */
attach(String address, long attachTimeout, long handshakeTimeout)187     public Connection attach(String address, long attachTimeout, long handshakeTimeout)
188         throws IOException {
189 
190         if (address == null) {
191             throw new NullPointerException("address is null");
192         }
193         if (attachTimeout < 0 || handshakeTimeout < 0) {
194             throw new IllegalArgumentException("timeout is negative");
195         }
196 
197         int splitIndex = address.indexOf(':');
198         String host;
199         String portStr;
200         if (splitIndex < 0) {
201             host = InetAddress.getLocalHost().getHostName();
202             portStr = address;
203         } else {
204             host = address.substring(0, splitIndex);
205             portStr = address.substring(splitIndex+1);
206         }
207 
208         int port;
209         try {
210             port = Integer.decode(portStr).intValue();
211         } catch (NumberFormatException e) {
212             throw new IllegalArgumentException(
213                 "unable to parse port number in address");
214         }
215 
216 
217         // open TCP connection to VM
218 
219         InetSocketAddress sa = new InetSocketAddress(host, port);
220         Socket s = new Socket();
221         try {
222             s.connect(sa, (int)attachTimeout);
223         } catch (SocketTimeoutException exc) {
224             try {
225                 s.close();
226             } catch (IOException x) { }
227             throw new TransportTimeoutException("timed out trying to establish connection");
228         }
229 
230         // handshake with the target VM
231         try {
232             handshake(s, handshakeTimeout);
233         } catch (IOException exc) {
234             try {
235                 s.close();
236             } catch (IOException x) { }
237             throw exc;
238         }
239 
240         return new SocketConnection(s);
241     }
242 
243     /*
244      * Listen on the specified address and port. Return a listener
245      * that encapsulates the ServerSocket.
246      */
startListening(String localaddress, int port)247     ListenKey startListening(String localaddress, int port) throws IOException {
248         InetSocketAddress sa;
249         if (localaddress == null) {
250             sa = new InetSocketAddress(port);
251         } else {
252             sa = new InetSocketAddress(localaddress, port);
253         }
254         ServerSocket ss = new ServerSocket();
255         ss.bind(sa);
256         return new SocketListenKey(ss);
257     }
258 
259     /**
260      * Listen on the specified address
261      */
startListening(String address)262     public ListenKey startListening(String address) throws IOException {
263         // use ephemeral port if address isn't specified.
264         if (address == null || address.length() == 0) {
265             address = "0";
266         }
267 
268         int splitIndex = address.indexOf(':');
269         String localaddr = null;
270         if (splitIndex >= 0) {
271             localaddr = address.substring(0, splitIndex);
272             address = address.substring(splitIndex+1);
273         }
274 
275         int port;
276         try {
277             port = Integer.decode(address).intValue();
278         } catch (NumberFormatException e) {
279             throw new IllegalArgumentException(
280                     "unable to parse port number in address");
281         }
282 
283         return startListening(localaddr, port);
284     }
285 
286     /**
287      * Listen on the default address
288      */
startListening()289     public ListenKey startListening() throws IOException {
290         return startListening(null, 0);
291     }
292 
293     /**
294      * Stop the listener
295      */
stopListening(ListenKey listener)296     public void stopListening(ListenKey listener) throws IOException {
297         if (!(listener instanceof SocketListenKey)) {
298             throw new IllegalArgumentException("Invalid listener");
299         }
300 
301         synchronized (listener) {
302             ServerSocket ss = ((SocketListenKey)listener).socket();
303 
304             // if the ServerSocket has been closed it means
305             // the listener is invalid
306             if (ss.isClosed()) {
307                 throw new IllegalArgumentException("Invalid listener");
308             }
309             ss.close();
310         }
311     }
312 
313     /**
314      * Accept a connection from a debuggee and handshake with it.
315      */
accept(ListenKey listener, long acceptTimeout, long handshakeTimeout)316     public Connection accept(ListenKey listener, long acceptTimeout, long handshakeTimeout) throws IOException {
317         if (acceptTimeout < 0 || handshakeTimeout < 0) {
318             throw new IllegalArgumentException("timeout is negative");
319         }
320         if (!(listener instanceof SocketListenKey)) {
321             throw new IllegalArgumentException("Invalid listener");
322         }
323         ServerSocket ss;
324 
325         // obtain the ServerSocket from the listener - if the
326         // socket is closed it means the listener is invalid
327         synchronized (listener) {
328             ss = ((SocketListenKey)listener).socket();
329             if (ss.isClosed()) {
330                throw new IllegalArgumentException("Invalid listener");
331             }
332         }
333 
334         // from here onwards it's possible that the ServerSocket
335         // may be closed by a call to stopListening - that's okay
336         // because the ServerSocket methods will throw an
337         // IOException indicating the socket is closed.
338         //
339         // Additionally, it's possible that another thread calls accept
340         // with a different accept timeout - that creates a same race
341         // condition between setting the timeout and calling accept.
342         // As it is such an unlikely scenario (requires both threads
343         // to be using the same listener we've chosen to ignore the issue).
344 
345         ss.setSoTimeout((int)acceptTimeout);
346         Socket s;
347         try {
348             s = ss.accept();
349         } catch (SocketTimeoutException x) {
350             throw new TransportTimeoutException("timeout waiting for connection");
351         }
352 
353         // handshake here
354         handshake(s, handshakeTimeout);
355 
356         return new SocketConnection(s);
357     }
358 
toString()359     public String toString() {
360        return name();
361     }
362 }
363 
364 
365 /*
366  * The Connection returned by attach and accept is one of these
367  */
368 class SocketConnection extends Connection {
369     private Socket socket;
370     private boolean closed = false;
371     private OutputStream socketOutput;
372     private InputStream socketInput;
373     private Object receiveLock = new Object();
374     private Object sendLock = new Object();
375     private Object closeLock = new Object();
376 
SocketConnection(Socket socket)377     SocketConnection(Socket socket) throws IOException {
378         this.socket = socket;
379         socket.setTcpNoDelay(true);
380         socketInput = socket.getInputStream();
381         socketOutput = socket.getOutputStream();
382     }
383 
close()384     public void close() throws IOException {
385         synchronized (closeLock) {
386            if (closed) {
387                 return;
388            }
389            socketOutput.close();
390            socketInput.close();
391            socket.close();
392            closed = true;
393         }
394     }
395 
isOpen()396     public boolean isOpen() {
397         synchronized (closeLock) {
398             return !closed;
399         }
400     }
401 
readPacket()402     public byte[] readPacket() throws IOException {
403         if (!isOpen()) {
404             throw new ClosedConnectionException("connection is closed");
405         }
406         synchronized (receiveLock) {
407             int b1,b2,b3,b4;
408 
409             // length
410             try {
411                 b1 = socketInput.read();
412                 b2 = socketInput.read();
413                 b3 = socketInput.read();
414                 b4 = socketInput.read();
415             } catch (IOException ioe) {
416                 if (!isOpen()) {
417                     throw new ClosedConnectionException("connection is closed");
418                 } else {
419                     throw ioe;
420                 }
421             }
422 
423             // EOF
424             if (b1<0) {
425                return new byte[0];
426             }
427 
428             if (b2<0 || b3<0 || b4<0) {
429                 throw new IOException("protocol error - premature EOF");
430             }
431 
432             int len = ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4 << 0));
433 
434             if (len < 0) {
435                 throw new IOException("protocol error - invalid length");
436             }
437 
438             byte b[] = new byte[len];
439             b[0] = (byte)b1;
440             b[1] = (byte)b2;
441             b[2] = (byte)b3;
442             b[3] = (byte)b4;
443 
444             int off = 4;
445             len -= off;
446 
447             while (len > 0) {
448                 int count;
449                 try {
450                     count = socketInput.read(b, off, len);
451                 } catch (IOException ioe) {
452                     if (!isOpen()) {
453                         throw new ClosedConnectionException("connection is closed");
454                     } else {
455                         throw ioe;
456                     }
457                 }
458                 if (count < 0) {
459                     throw new IOException("protocol error - premature EOF");
460                 }
461                 len -= count;
462                 off += count;
463             }
464 
465             return b;
466         }
467     }
468 
writePacket(byte b[])469     public void writePacket(byte b[]) throws IOException {
470         if (!isOpen()) {
471             throw new ClosedConnectionException("connection is closed");
472         }
473 
474         /*
475          * Check the packet size
476          */
477         if (b.length < 11) {
478             throw new IllegalArgumentException("packet is insufficient size");
479         }
480         int b0 = b[0] & 0xff;
481         int b1 = b[1] & 0xff;
482         int b2 = b[2] & 0xff;
483         int b3 = b[3] & 0xff;
484         int len = ((b0 << 24) | (b1 << 16) | (b2 << 8) | (b3 << 0));
485         if (len < 11) {
486             throw new IllegalArgumentException("packet is insufficient size");
487         }
488 
489         /*
490          * Check that the byte array contains the complete packet
491          */
492         if (len > b.length) {
493             throw new IllegalArgumentException("length mis-match");
494         }
495 
496         synchronized (sendLock) {
497             try {
498                 /*
499                  * Send the packet (ignoring any bytes that follow
500                  * the packet in the byte array).
501                  */
502                 socketOutput.write(b, 0, len);
503             } catch (IOException ioe) {
504                 if (!isOpen()) {
505                     throw new ClosedConnectionException("connection is closed");
506                 } else {
507                     throw ioe;
508                 }
509             }
510         }
511     }
512 }
513 
514 
515 /*
516  * The capabilities of the socket transport service
517  */
518 class SocketTransportServiceCapabilities extends TransportService.Capabilities {
519 
supportsMultipleConnections()520     public boolean supportsMultipleConnections() {
521         return true;
522     }
523 
supportsAttachTimeout()524     public boolean supportsAttachTimeout() {
525         return true;
526     }
527 
supportsAcceptTimeout()528     public boolean supportsAcceptTimeout() {
529         return true;
530     }
531 
supportsHandshakeTimeout()532     public boolean supportsHandshakeTimeout() {
533         return true;
534     }
535 
536 }
537