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