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