Path: blob/master/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java
41139 views
/*1* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*/2728package sun.nio.ch;2930import java.io.IOException;31import java.net.InetAddress;32import java.net.InetSocketAddress;33import java.net.SocketAddress;34import java.net.UnixDomainSocketAddress;35import java.net.StandardProtocolFamily;36import java.net.StandardSocketOptions;37import java.nio.*;38import java.nio.channels.*;39import java.nio.file.Files;40import java.nio.file.Path;41import java.nio.channels.spi.*;42import java.security.AccessController;43import java.security.PrivilegedExceptionAction;44import java.security.PrivilegedActionException;45import java.security.SecureRandom;46import java.util.Random;474849/**50* A simple Pipe implementation based on a socket connection.51*/5253class PipeImpl54extends Pipe55{56// Number of bytes in the secret handshake.57private static final int NUM_SECRET_BYTES = 16;5859// Random object for handshake values60private static final Random RANDOM_NUMBER_GENERATOR = new SecureRandom();6162// Source and sink channels63private final SourceChannelImpl source;64private final SinkChannelImpl sink;6566private static class Initializer67implements PrivilegedExceptionAction<Void>68{6970private final SelectorProvider sp;71private IOException ioe;72SourceChannelImpl source;73SinkChannelImpl sink;7475private Initializer(SelectorProvider sp) {76this.sp = sp;77}7879@Override80public Void run() throws IOException {81LoopbackConnector connector = new LoopbackConnector();82connector.run();83if (ioe instanceof ClosedByInterruptException) {84ioe = null;85Thread connThread = new Thread(connector) {86@Override87public void interrupt() {}88};89connThread.start();90for (;;) {91try {92connThread.join();93break;94} catch (InterruptedException ex) {}95}96Thread.currentThread().interrupt();97}9899if (ioe != null)100throw new IOException("Unable to establish loopback connection", ioe);101102return null;103}104105private class LoopbackConnector implements Runnable {106107@Override108public void run() {109ServerSocketChannel ssc = null;110SocketChannel sc1 = null;111SocketChannel sc2 = null;112// Loopback address113SocketAddress sa = null;114115try {116// Create secret with a backing array.117ByteBuffer secret = ByteBuffer.allocate(NUM_SECRET_BYTES);118ByteBuffer bb = ByteBuffer.allocate(NUM_SECRET_BYTES);119120for(;;) {121// Bind ServerSocketChannel to a port on the loopback122// address123if (ssc == null || !ssc.isOpen()) {124ssc = createListener();125sa = ssc.getLocalAddress();126}127128// Establish connection (assume connections are eagerly129// accepted)130sc1 = SocketChannel.open(sa);131RANDOM_NUMBER_GENERATOR.nextBytes(secret.array());132do {133sc1.write(secret);134} while (secret.hasRemaining());135secret.rewind();136137// Get a connection and verify it is legitimate138sc2 = ssc.accept();139do {140sc2.read(bb);141} while (bb.hasRemaining());142bb.rewind();143144if (bb.equals(secret))145break;146147sc2.close();148sc1.close();149}150151// Create source and sink channels152source = new SourceChannelImpl(sp, sc1);153sink = new SinkChannelImpl(sp, sc2);154} catch (IOException e) {155try {156if (sc1 != null)157sc1.close();158if (sc2 != null)159sc2.close();160} catch (IOException e2) {}161ioe = e;162} finally {163try {164if (ssc != null)165ssc.close();166if (sa instanceof UnixDomainSocketAddress) {167Path path = ((UnixDomainSocketAddress) sa).getPath();168Files.deleteIfExists(path);169}170} catch (IOException e2) {}171}172}173}174}175176/**177* Creates a Pipe implementation that supports buffering.178*/179PipeImpl(SelectorProvider sp) throws IOException {180this(sp, true);181}182183/**184* Creates Pipe implementation that supports optionally buffering.185*186* @implNote The pipe uses Unix domain sockets where possible. It uses a187* loopback connection on older editions of Windows. When buffering is188* disabled then it sets TCP_NODELAY on the sink channel.189*/190@SuppressWarnings("removal")191PipeImpl(SelectorProvider sp, boolean buffering) throws IOException {192Initializer initializer = new Initializer(sp);193try {194AccessController.doPrivileged(initializer);195SinkChannelImpl sink = initializer.sink;196if (sink.isNetSocket() && !buffering) {197sink.setOption(StandardSocketOptions.TCP_NODELAY, true);198}199} catch (PrivilegedActionException pae) {200throw (IOException) pae.getCause();201}202this.source = initializer.source;203this.sink = initializer.sink;204}205206public SourceChannelImpl source() {207return source;208}209210public SinkChannelImpl sink() {211return sink;212}213214private static volatile boolean noUnixDomainSockets;215216private static ServerSocketChannel createListener() throws IOException {217ServerSocketChannel listener = null;218if (!noUnixDomainSockets) {219try {220listener = ServerSocketChannel.open(StandardProtocolFamily.UNIX);221return listener.bind(null);222} catch (UnsupportedOperationException | IOException e) {223// IOException is most likely to be caused by the temporary directory224// name being too long. Possibly should log this.225noUnixDomainSockets = true;226if (listener != null)227listener.close();228}229}230listener = ServerSocketChannel.open();231InetAddress lb = InetAddress.getLoopbackAddress();232listener.bind(new InetSocketAddress(lb, 0));233return listener;234}235}236237238