1 /* 2 * Copyright (c) 2001, 2019, 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 sun.nio.ch; 27 28 import java.io.IOException; 29 import java.io.UncheckedIOException; 30 import java.lang.invoke.MethodHandles; 31 import java.lang.invoke.MethodHandles.Lookup; 32 import java.lang.invoke.VarHandle; 33 import java.net.DatagramPacket; 34 import java.net.DatagramSocket; 35 import java.net.DatagramSocketImpl; 36 import java.net.InetAddress; 37 import java.net.InetSocketAddress; 38 import java.net.NetworkInterface; 39 import java.net.SocketAddress; 40 import java.net.SocketException; 41 import java.net.SocketOption; 42 import java.net.StandardSocketOptions; 43 import java.nio.ByteBuffer; 44 import java.nio.channels.AlreadyConnectedException; 45 import java.nio.channels.ClosedChannelException; 46 import java.nio.channels.DatagramChannel; 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 import java.util.Set; 50 51 import static java.util.concurrent.TimeUnit.MILLISECONDS; 52 53 // Make a datagram-socket channel look like a datagram socket. 54 // 55 // The methods in this class are defined in exactly the same order as in 56 // java.net.DatagramSocket so as to simplify tracking future changes to that 57 // class. 58 // 59 60 class DatagramSocketAdaptor 61 extends DatagramSocket 62 { 63 // The channel being adapted 64 private final DatagramChannelImpl dc; 65 66 // Timeout "option" value for receives 67 private volatile int timeout; 68 69 // create DatagramSocket with useless impl 70 private DatagramSocketAdaptor(DatagramChannelImpl dc) { 71 super(new DummyDatagramSocketImpl()); 72 this.dc = dc; 73 } 74 75 static DatagramSocket create(DatagramChannelImpl dc) { 76 return new DatagramSocketAdaptor(dc); 77 } 78 79 private void connectInternal(SocketAddress remote) throws SocketException { 80 try { 81 dc.connect(remote, false); // skips check for already connected 82 } catch (ClosedChannelException e) { 83 // ignore 84 } catch (Exception x) { 85 Net.translateToSocketException(x); 86 } 87 } 88 89 @Override 90 public void bind(SocketAddress local) throws SocketException { 91 if (local != null) { 92 local = Net.asInetSocketAddress(local); 93 } else { 94 local = new InetSocketAddress(0); 95 } 96 try { 97 dc.bind(local); 98 } catch (Exception x) { 99 Net.translateToSocketException(x); 100 } 101 } 102 103 @Override 104 public void connect(InetAddress address, int port) { 105 if (address == null) 106 throw new IllegalArgumentException("Address can't be null"); 107 try { 108 connectInternal(new InetSocketAddress(address, port)); 109 } catch (SocketException x) { 110 throw new UncheckedIOException(x); 111 } 112 } 113 114 @Override 115 public void connect(SocketAddress remote) throws SocketException { 116 if (remote == null) 117 throw new IllegalArgumentException("Address can't be null"); 118 connectInternal(Net.asInetSocketAddress(remote)); 119 } 120 121 @Override 122 public void disconnect() { 123 try { 124 dc.disconnect(); 125 } catch (IOException x) { 126 throw new UncheckedIOException(x); 127 } 128 } 129 130 @Override 131 public boolean isBound() { 132 return dc.localAddress() != null; 133 } 134 135 @Override 136 public boolean isConnected() { 137 return dc.remoteAddress() != null; 138 } 139 140 @Override 141 public InetAddress getInetAddress() { 142 InetSocketAddress remote = dc.remoteAddress(); 143 return (remote != null) ? remote.getAddress() : null; 144 } 145 146 @Override 147 public int getPort() { 148 InetSocketAddress remote = dc.remoteAddress(); 149 return (remote != null) ? remote.getPort() : -1; 150 } 151 152 @Override 153 public SocketAddress getRemoteSocketAddress() { 154 return dc.remoteAddress(); 155 } 156 157 @Override 158 public SocketAddress getLocalSocketAddress() { 159 try { 160 return dc.getLocalAddress(); 161 } catch (ClosedChannelException e) { 162 return null; 163 } catch (Exception x) { 164 throw new Error(x); 165 } 166 } 167 168 @Override 169 public void send(DatagramPacket p) throws IOException { 170 ByteBuffer bb = null; 171 try { 172 InetSocketAddress target; 173 synchronized (p) { 174 // copy bytes to temporary direct buffer 175 int len = p.getLength(); 176 bb = Util.getTemporaryDirectBuffer(len); 177 bb.put(p.getData(), p.getOffset(), len); 178 bb.flip(); 179 180 // target address 181 if (p.getAddress() == null) { 182 InetSocketAddress remote = dc.remoteAddress(); 183 if (remote == null) { 184 // not specified by DatagramSocket 185 throw new IllegalArgumentException("Address not set"); 186 } 187 // set address/port to maintain compatibility with DatagramSocket 188 p.setAddress(remote.getAddress()); 189 p.setPort(remote.getPort()); 190 target = remote; 191 } else { 192 // throws IllegalArgumentException if port not set 193 target = (InetSocketAddress) p.getSocketAddress(); 194 } 195 } 196 // send datagram 197 try { 198 dc.blockingSend(bb, target); 199 } catch (AlreadyConnectedException e) { 200 throw new IllegalArgumentException("Connected and packet address differ"); 201 } catch (ClosedChannelException e) { 202 var exc = new SocketException("Socket closed"); 203 exc.initCause(e); 204 throw exc; 205 } 206 } finally { 207 if (bb != null) { 208 Util.offerFirstTemporaryDirectBuffer(bb); 209 } 210 } 211 } 212 213 @Override 214 public void receive(DatagramPacket p) throws IOException { 215 // get temporary direct buffer with a capacity of p.bufLength 216 int bufLength = DatagramPackets.getBufLength(p); 217 ByteBuffer bb = Util.getTemporaryDirectBuffer(bufLength); 218 try { 219 long nanos = MILLISECONDS.toNanos(timeout); 220 SocketAddress sender = dc.blockingReceive(bb, nanos); 221 bb.flip(); 222 synchronized (p) { 223 // copy bytes to the DatagramPacket and set length 224 int len = Math.min(bb.limit(), DatagramPackets.getBufLength(p)); 225 bb.get(p.getData(), p.getOffset(), len); 226 DatagramPackets.setLength(p, len); 227 228 // sender address 229 p.setSocketAddress(sender); 230 } 231 } catch (ClosedChannelException e) { 232 var exc = new SocketException("Socket closed"); 233 exc.initCause(e); 234 throw exc; 235 } finally { 236 Util.offerFirstTemporaryDirectBuffer(bb); 237 } 238 } 239 240 @Override 241 public InetAddress getLocalAddress() { 242 if (isClosed()) 243 return null; 244 InetSocketAddress local = dc.localAddress(); 245 if (local == null) 246 local = new InetSocketAddress(0); 247 InetAddress result = local.getAddress(); 248 SecurityManager sm = System.getSecurityManager(); 249 if (sm != null) { 250 try { 251 sm.checkConnect(result.getHostAddress(), -1); 252 } catch (SecurityException x) { 253 return new InetSocketAddress(0).getAddress(); 254 } 255 } 256 return result; 257 } 258 259 @Override 260 public int getLocalPort() { 261 if (isClosed()) 262 return -1; 263 InetSocketAddress local = dc.localAddress(); 264 if (local != null) { 265 return local.getPort(); 266 } 267 return 0; 268 } 269 270 @Override 271 public void setSoTimeout(int timeout) throws SocketException { 272 if (isClosed()) 273 throw new SocketException("Socket is closed"); 274 if (timeout < 0) 275 throw new IllegalArgumentException("timeout < 0"); 276 this.timeout = timeout; 277 } 278 279 @Override 280 public int getSoTimeout() throws SocketException { 281 if (isClosed()) 282 throw new SocketException("Socket is closed"); 283 return timeout; 284 } 285 286 private void setBooleanOption(SocketOption<Boolean> name, boolean value) 287 throws SocketException 288 { 289 try { 290 dc.setOption(name, value); 291 } catch (IOException x) { 292 Net.translateToSocketException(x); 293 } 294 } 295 296 private void setIntOption(SocketOption<Integer> name, int value) 297 throws SocketException 298 { 299 try { 300 dc.setOption(name, value); 301 } catch (IOException x) { 302 Net.translateToSocketException(x); 303 } 304 } 305 306 private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException { 307 try { 308 return dc.getOption(name).booleanValue(); 309 } catch (IOException x) { 310 Net.translateToSocketException(x); 311 return false; // keep compiler happy 312 } 313 } 314 315 private int getIntOption(SocketOption<Integer> name) throws SocketException { 316 try { 317 return dc.getOption(name).intValue(); 318 } catch (IOException x) { 319 Net.translateToSocketException(x); 320 return -1; // keep compiler happy 321 } 322 } 323 324 @Override 325 public void setSendBufferSize(int size) throws SocketException { 326 if (size <= 0) 327 throw new IllegalArgumentException("Invalid send size"); 328 setIntOption(StandardSocketOptions.SO_SNDBUF, size); 329 } 330 331 @Override 332 public int getSendBufferSize() throws SocketException { 333 return getIntOption(StandardSocketOptions.SO_SNDBUF); 334 } 335 336 @Override 337 public void setReceiveBufferSize(int size) throws SocketException { 338 if (size <= 0) 339 throw new IllegalArgumentException("Invalid receive size"); 340 setIntOption(StandardSocketOptions.SO_RCVBUF, size); 341 } 342 343 @Override 344 public int getReceiveBufferSize() throws SocketException { 345 return getIntOption(StandardSocketOptions.SO_RCVBUF); 346 } 347 348 @Override 349 public void setReuseAddress(boolean on) throws SocketException { 350 setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); 351 } 352 353 @Override 354 public boolean getReuseAddress() throws SocketException { 355 return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); 356 } 357 358 @Override 359 public void setBroadcast(boolean on) throws SocketException { 360 setBooleanOption(StandardSocketOptions.SO_BROADCAST, on); 361 } 362 363 @Override 364 public boolean getBroadcast() throws SocketException { 365 return getBooleanOption(StandardSocketOptions.SO_BROADCAST); 366 } 367 368 @Override 369 public void setTrafficClass(int tc) throws SocketException { 370 setIntOption(StandardSocketOptions.IP_TOS, tc); 371 } 372 373 @Override 374 public int getTrafficClass() throws SocketException { 375 return getIntOption(StandardSocketOptions.IP_TOS); 376 } 377 378 @Override 379 public void close() { 380 try { 381 dc.close(); 382 } catch (IOException x) { 383 throw new Error(x); 384 } 385 } 386 387 @Override 388 public boolean isClosed() { 389 return !dc.isOpen(); 390 } 391 392 @Override 393 public DatagramChannel getChannel() { 394 return dc; 395 } 396 397 @Override 398 public <T> DatagramSocket setOption(SocketOption<T> name, T value) throws IOException { 399 dc.setOption(name, value); 400 return this; 401 } 402 403 @Override 404 public <T> T getOption(SocketOption<T> name) throws IOException { 405 return dc.getOption(name); 406 } 407 408 @Override 409 public Set<SocketOption<?>> supportedOptions() { 410 return dc.supportedOptions(); 411 } 412 413 414 /** 415 * DatagramSocketImpl implementation where all methods throw an error. 416 */ 417 private static class DummyDatagramSocketImpl extends DatagramSocketImpl { 418 private static <T> T shouldNotGetHere() { 419 throw new InternalError("Should not get here"); 420 } 421 422 @Override 423 protected void create() { 424 shouldNotGetHere(); 425 } 426 427 @Override 428 protected void bind(int lport, InetAddress laddr) { 429 shouldNotGetHere(); 430 } 431 432 @Override 433 protected void send(DatagramPacket p) { 434 shouldNotGetHere(); 435 } 436 437 @Override 438 protected int peek(InetAddress address) { 439 return shouldNotGetHere(); 440 } 441 442 @Override 443 protected int peekData(DatagramPacket p) { 444 return shouldNotGetHere(); 445 } 446 447 @Override 448 protected void receive(DatagramPacket p) { 449 shouldNotGetHere(); 450 } 451 452 @Deprecated 453 protected void setTTL(byte ttl) { 454 shouldNotGetHere(); 455 } 456 457 @Deprecated 458 protected byte getTTL() { 459 return shouldNotGetHere(); 460 } 461 462 @Override 463 protected void setTimeToLive(int ttl) { 464 shouldNotGetHere(); 465 } 466 467 @Override 468 protected int getTimeToLive() { 469 return shouldNotGetHere(); 470 } 471 472 @Override 473 protected void join(InetAddress group) { 474 shouldNotGetHere(); 475 } 476 477 @Override 478 protected void leave(InetAddress inetaddr) { 479 shouldNotGetHere(); 480 } 481 482 @Override 483 protected void joinGroup(SocketAddress group, NetworkInterface netIf) { 484 shouldNotGetHere(); 485 } 486 487 @Override 488 protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) { 489 shouldNotGetHere(); 490 } 491 492 @Override 493 protected void close() { 494 shouldNotGetHere(); 495 } 496 497 @Override 498 public Object getOption(int optID) { 499 return shouldNotGetHere(); 500 } 501 502 @Override 503 public void setOption(int optID, Object value) { 504 shouldNotGetHere(); 505 } 506 507 @Override 508 protected <T> void setOption(SocketOption<T> name, T value) { 509 shouldNotGetHere(); 510 } 511 512 @Override 513 protected <T> T getOption(SocketOption<T> name) { 514 return shouldNotGetHere(); 515 } 516 517 @Override 518 protected Set<SocketOption<?>> supportedOptions() { 519 return shouldNotGetHere(); 520 } 521 } 522 523 /** 524 * Defines static methods to get/set DatagramPacket fields and workaround 525 * DatagramPacket deficiencies. 526 */ 527 private static class DatagramPackets { 528 private static final VarHandle LENGTH; 529 private static final VarHandle BUF_LENGTH; 530 static { 531 try { 532 PrivilegedAction<Lookup> pa = () -> { 533 try { 534 return MethodHandles.privateLookupIn(DatagramPacket.class, MethodHandles.lookup()); 535 } catch (Exception e) { 536 throw new ExceptionInInitializerError(e); 537 } 538 }; 539 MethodHandles.Lookup l = AccessController.doPrivileged(pa); 540 LENGTH = l.findVarHandle(DatagramPacket.class, "length", int.class); 541 BUF_LENGTH = l.findVarHandle(DatagramPacket.class, "bufLength", int.class); 542 } catch (Exception e) { 543 throw new ExceptionInInitializerError(e); 544 } 545 } 546 547 /** 548 * Sets the DatagramPacket.length field. DatagramPacket.setLength cannot be 549 * used at this time because it sets both the length and bufLength fields. 550 */ 551 static void setLength(DatagramPacket p, int value) { 552 synchronized (p) { 553 LENGTH.set(p, value); 554 } 555 } 556 557 /** 558 * Returns the value of the DatagramPacket.bufLength field. 559 */ 560 static int getBufLength(DatagramPacket p) { 561 synchronized (p) { 562 return (int) BUF_LENGTH.get(p); 563 } 564 } 565 } 566 }