1 /*
  2  * Copyright (c) 2000, 2025, 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.FileDescriptor;
 29 import java.io.IOException;
 30 import java.net.BindException;
 31 import java.net.InetSocketAddress;
 32 import java.net.ProtocolFamily;
 33 import java.net.ServerSocket;
 34 import java.net.SocketAddress;
 35 import java.net.SocketOption;
 36 import java.net.SocketTimeoutException;
 37 import java.net.StandardSocketOptions;
 38 import java.net.UnixDomainSocketAddress;
 39 import java.nio.channels.AlreadyBoundException;
 40 import java.nio.channels.AsynchronousCloseException;
 41 import java.nio.channels.ClosedChannelException;
 42 import java.nio.channels.IllegalBlockingModeException;
 43 import java.nio.channels.NotYetBoundException;
 44 import java.nio.channels.SelectionKey;
 45 import java.nio.channels.ServerSocketChannel;
 46 import java.nio.channels.SocketChannel;
 47 import java.nio.channels.spi.SelectorProvider;
 48 import java.nio.file.Path;
 49 import java.util.Collections;
 50 import java.util.HashSet;
 51 import java.util.Set;
 52 import java.util.Objects;
 53 import java.util.concurrent.locks.ReentrantLock;
 54 import static java.net.StandardProtocolFamily.INET;
 55 import static java.net.StandardProtocolFamily.INET6;
 56 import static java.net.StandardProtocolFamily.UNIX;
 57 
 58 import sun.net.NetHooks;
 59 import sun.net.ext.ExtendedSocketOptions;
 60 
 61 /**
 62  * An implementation of ServerSocketChannels
 63  */
 64 
 65 class ServerSocketChannelImpl
 66     extends ServerSocketChannel
 67     implements SelChImpl
 68 {
 69     // Used to make native close and configure calls
 70     private static final NativeDispatcher nd = new SocketDispatcher();
 71 
 72     // The protocol family of the socket
 73     private final ProtocolFamily family;
 74 
 75     // Our file descriptor
 76     private final FileDescriptor fd;
 77     private final int fdVal;
 78 
 79     // Lock held by thread currently blocked on this channel
 80     private final ReentrantLock acceptLock = new ReentrantLock();
 81 
 82     // Lock held by any thread that modifies the state fields declared below
 83     // DO NOT invoke a blocking I/O operation while holding this lock!
 84     private final Object stateLock = new Object();
 85 
 86     // -- The following fields are protected by stateLock
 87 
 88     // Channel state, increases monotonically
 89     private static final int ST_INUSE = 0;
 90     private static final int ST_CLOSING = 1;
 91     private static final int ST_CLOSED = 2;
 92     private int state;
 93 
 94     // ID of native thread currently blocked in this channel, for signalling
 95     private NativeThread thread;
 96 
 97     // Binding
 98     private SocketAddress localAddress; // null => unbound
 99 
100     // set true when exclusive binding is on and SO_REUSEADDR is emulated
101     private boolean isReuseAddress;
102 
103     // Our socket adaptor, if any
104     private ServerSocket socket;
105 
106     // True if the channel's socket has been forced into non-blocking mode
107     // by a virtual thread. It cannot be reset. When the channel is in
108     // blocking mode and the channel's socket is in non-blocking mode then
109     // operations that don't complete immediately will poll the socket and
110     // preserve the semantics of blocking operations.
111     private volatile boolean forcedNonBlocking;
112 
113     // -- End of fields protected by stateLock
114 
115     ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
116         this(sp, Net.isIPv6Available() ? INET6 : INET);
117     }
118 
119     ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family)
120         throws IOException
121     {
122         super(sp);
123         Objects.requireNonNull(family, "'family' is null");
124         if ((family != INET) && (family != INET6) && (family != UNIX)) {
125             throw new UnsupportedOperationException("Protocol family not supported");
126         }
127         if (family == INET6 && !Net.isIPv6Available()) {
128             throw new UnsupportedOperationException("IPv6 not available");
129         }
130 
131         this.family = family;
132         if (family == UNIX) {
133             this.fd = UnixDomainSockets.socket();
134         } else {
135             this.fd = Net.serverSocket(family);
136         }
137         this.fdVal = IOUtil.fdVal(fd);
138     }
139 
140     ServerSocketChannelImpl(SelectorProvider sp,
141                             ProtocolFamily family,
142                             FileDescriptor fd,
143                             boolean bound)
144         throws IOException
145     {
146         super(sp);
147 
148         if (family == UNIX) {
149             this.family = UNIX;
150         } else {
151             this.family = Net.isIPv6Available() ? INET6 : INET;
152         }
153         this.fd = fd;
154         this.fdVal = IOUtil.fdVal(fd);
155 
156         if (bound) {
157             synchronized (stateLock) {
158                 if (family == UNIX) {
159                     localAddress = UnixDomainSockets.localAddress(fd);
160                 } else {
161                     localAddress = Net.localAddress(fd);
162                 }
163             }
164         }
165     }
166 
167     /**
168      * Returns true if this channel is to a INET or INET6 socket.
169      */
170     private boolean isNetSocket() {
171         return (family == INET) || (family == INET6);
172     }
173 
174     /**
175      * Returns true if this channel is to a UNIX socket.
176      */
177     boolean isUnixSocket() {
178         return (family == UNIX);
179     }
180 
181     // @throws ClosedChannelException if channel is closed
182     private void ensureOpen() throws ClosedChannelException {
183         if (!isOpen())
184             throw new ClosedChannelException();
185     }
186 
187     @Override
188     public ServerSocket socket() {
189         synchronized (stateLock) {
190             if (socket == null) {
191                 if (isNetSocket()) {
192                     socket = ServerSocketAdaptor.create(this);
193                 } else {
194                     throw new UnsupportedOperationException("Not supported");
195                 }
196             }
197             return socket;
198         }
199     }
200 
201     @Override
202     public SocketAddress getLocalAddress() throws IOException {
203         synchronized (stateLock) {
204             ensureOpen();
205             return localAddress;
206         }
207     }
208 
209     @Override
210     public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
211         throws IOException
212     {
213         Objects.requireNonNull(name);
214         if (!supportedOptions().contains(name))
215             throw new UnsupportedOperationException("'" + name + "' not supported");
216         if (!name.type().isInstance(value))
217             throw new IllegalArgumentException("Invalid value '" + value + "'");
218 
219         synchronized (stateLock) {
220             ensureOpen();
221             if (isNetSocket()
222                     && name == StandardSocketOptions.SO_REUSEADDR
223                     && Net.useExclusiveBind()) {
224                 // SO_REUSEADDR emulated when using exclusive bind
225                 isReuseAddress = (Boolean) value;
226             } else {
227                 // no options that require special handling
228                 Net.setSocketOption(fd, Net.UNSPEC, name, value);
229             }
230             return this;
231         }
232     }
233 
234     @Override
235     @SuppressWarnings("unchecked")
236     public <T> T getOption(SocketOption<T> name)
237         throws IOException
238     {
239         Objects.requireNonNull(name);
240         if (!supportedOptions().contains(name))
241             throw new UnsupportedOperationException("'" + name + "' not supported");
242 
243         synchronized (stateLock) {
244             ensureOpen();
245             if (isNetSocket()
246                     && name == StandardSocketOptions.SO_REUSEADDR
247                     && Net.useExclusiveBind()) {
248                 // SO_REUSEADDR emulated when using exclusive bind
249                 return (T) Boolean.valueOf(isReuseAddress);
250             } else {
251                 // no options that require special handling
252                 return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
253             }
254         }
255     }
256 
257     private static class DefaultOptionsHolder {
258         static final Set<SocketOption<?>> defaultInetOptions = defaultInetOptions();
259         static final Set<SocketOption<?>> defaultUnixDomainOptions = defaultUnixDomainOptions();
260 
261         private static Set<SocketOption<?>> defaultInetOptions() {
262             HashSet<SocketOption<?>> set = new HashSet<>();
263             set.add(StandardSocketOptions.SO_RCVBUF);
264             set.add(StandardSocketOptions.SO_REUSEADDR);
265             if (Net.isReusePortAvailable()) {
266                 set.add(StandardSocketOptions.SO_REUSEPORT);
267             }
268             set.addAll(ExtendedSocketOptions.serverSocketOptions());
269             return Collections.unmodifiableSet(set);
270         }
271 
272         private static Set<SocketOption<?>> defaultUnixDomainOptions() {
273             HashSet<SocketOption<?>> set = new HashSet<>();
274             set.add(StandardSocketOptions.SO_RCVBUF);
275             return Collections.unmodifiableSet(set);
276         }
277     }
278 
279     @Override
280     public final Set<SocketOption<?>> supportedOptions() {
281         if (isUnixSocket()) {
282             return DefaultOptionsHolder.defaultUnixDomainOptions;
283         } else {
284             return DefaultOptionsHolder.defaultInetOptions;
285         }
286     }
287 
288     @Override
289     public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
290         synchronized (stateLock) {
291             ensureOpen();
292             if (localAddress != null)
293                 throw new AlreadyBoundException();
294             if (isUnixSocket()) {
295                 localAddress = unixBind(local, backlog);
296             } else {
297                 localAddress = netBind(local, backlog);
298             }
299         }
300         return this;
301     }
302 
303     private SocketAddress unixBind(SocketAddress local, int backlog) throws IOException {
304         if (local == null) {
305             // Attempt up to 10 times to find an unused name in temp directory.
306             // If local address supplied then bind called only once
307             boolean bound = false;
308             int attempts = 0;
309             while (attempts < 10 && !bound) {
310                 try {
311                     Path path = UnixDomainSockets.generateTempName().getPath();
312                     UnixDomainSockets.bind(fd, path);
313                     bound = true;
314                 } catch (BindException e) { }
315                 attempts++;
316             }
317             if (!bound)
318                 throw new BindException("Could not bind to temporary name");
319         } else {
320             Path path = UnixDomainSockets.checkAddress(local).getPath();
321             UnixDomainSockets.bind(fd, path);
322         }
323         Net.listen(fd, backlog < 1 ? 50 : backlog);
324         return UnixDomainSockets.localAddress(fd);
325     }
326 
327     private SocketAddress netBind(SocketAddress local, int backlog) throws IOException {
328         InetSocketAddress isa;
329         if (local == null) {
330             isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
331         } else {
332             isa = Net.checkAddress(local, family);
333         }
334         NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
335         Net.bind(family, fd, isa.getAddress(), isa.getPort());
336         Net.listen(fd, backlog < 1 ? 50 : backlog);
337         return Net.localAddress(fd);
338     }
339 
340     /**
341      * Marks the beginning of an I/O operation that might block.
342      *
343      * @throws ClosedChannelException if the channel is closed
344      * @throws NotYetBoundException if the channel's socket has not been bound yet
345      */
346     private void begin(boolean blocking) throws ClosedChannelException {
347         if (blocking)
348             begin();  // set blocker to close channel if interrupted
349         synchronized (stateLock) {
350             ensureOpen();
351             if (localAddress == null)
352                 throw new NotYetBoundException();
353             if (blocking)
354                 thread = NativeThread.current();
355         }
356     }
357 
358     /**
359      * Marks the end of an I/O operation that may have blocked.
360      *
361      * @throws AsynchronousCloseException if the channel was closed due to this
362      * thread being interrupted on a blocking I/O operation.
363      */
364     private void end(boolean blocking, boolean completed)
365         throws AsynchronousCloseException
366     {
367         if (blocking) {
368             synchronized (stateLock) {
369                 thread = null;
370                 if (state == ST_CLOSING) {
371                     tryFinishClose();
372                 }
373             }
374             end(completed);
375         }
376     }
377 
378     @Override
379     public SocketChannel accept() throws IOException {
380         int n = 0;
381         FileDescriptor newfd = new FileDescriptor();
382         SocketAddress[] saa = new SocketAddress[1];
383 
384         acceptLock.lock();
385         try {
386             ensureOpen();
387             boolean blocking = isBlocking();
388             try {
389                 begin(blocking);
390                 configureSocketNonBlockingIfVirtualThread();
391                 n = implAccept(this.fd, newfd, saa);
392                 if (blocking) {
393                     while (IOStatus.okayToRetry(n) && isOpen()) {
394                         park(Net.POLLIN);
395                         n = implAccept(this.fd, newfd, saa);
396                     }
397                 }
398             } finally {
399                 end(blocking, n > 0);
400                 assert IOStatus.check(n);
401             }
402         } finally {
403             acceptLock.unlock();
404         }
405 
406         if (n > 0) {
407             return finishAccept(newfd, saa[0]);
408         } else {
409             return null;
410         }
411     }
412 
413     private int implAccept(FileDescriptor fd, FileDescriptor newfd, SocketAddress[] saa)
414         throws IOException
415     {
416         if (isUnixSocket()) {
417             String[] pa = new String[1];
418             int n = UnixDomainSockets.accept(fd, newfd, pa);
419             if (n > 0)
420                 saa[0] = UnixDomainSocketAddress.of(pa[0]);
421             return n;
422         } else {
423             InetSocketAddress[] issa = new InetSocketAddress[1];
424             int n = Net.accept(fd, newfd, issa);
425             if (n > 0)
426                 saa[0] = issa[0];
427             return n;
428         }
429     }
430 
431     /**
432      * Accepts a new connection with a given timeout. This method requires the
433      * channel to be configured in blocking mode.
434      *
435      * @apiNote This method is for use by the socket adaptor.
436      *
437      * @param nanos the timeout, in nanoseconds
438      * @throws IllegalBlockingModeException if the channel is configured non-blocking
439      * @throws SocketTimeoutException if the timeout expires
440      */
441     SocketChannel blockingAccept(long nanos) throws IOException {
442         int n = 0;
443         FileDescriptor newfd = new FileDescriptor();
444         SocketAddress[] saa = new SocketAddress[1];
445 
446         acceptLock.lock();
447         try {
448             // check that channel is configured blocking
449             if (!isBlocking())
450                 throw new IllegalBlockingModeException();
451 
452             try {
453                 begin(true);
454                 // change socket to non-blocking
455                 lockedConfigureBlocking(false);
456                 try {
457                     long startNanos = System.nanoTime();
458                     n = implAccept(fd, newfd, saa);
459                     while (n == IOStatus.UNAVAILABLE && isOpen()) {
460                         long remainingNanos = nanos - (System.nanoTime() - startNanos);
461                         if (remainingNanos <= 0) {
462                             throw new SocketTimeoutException("Accept timed out");
463                         }
464                         park(Net.POLLIN, remainingNanos);
465                         n = implAccept(fd, newfd, saa);
466                     }
467                 } finally {
468                     // restore socket to blocking mode (if channel is open)
469                     tryLockedConfigureBlocking(true);
470                 }
471             } finally {
472                 end(true, n > 0);
473             }
474         } finally {
475             acceptLock.unlock();
476         }
477 
478         assert n > 0;
479         return finishAccept(newfd, saa[0]);
480     }
481 
482     private SocketChannel finishAccept(FileDescriptor newfd, SocketAddress sa)
483         throws IOException
484     {
485         try {
486             // newly accepted socket is initially in blocking mode
487             IOUtil.configureBlocking(newfd, true);
488             return new SocketChannelImpl(provider(), family, newfd, sa);
489         } catch (Exception e) {
490             nd.close(newfd);
491             throw e;
492         }
493     }
494 
495     @Override
496     protected void implConfigureBlocking(boolean block) throws IOException {
497         acceptLock.lock();
498         try {
499             lockedConfigureBlocking(block);
500         } finally {
501             acceptLock.unlock();
502         }
503     }
504 
505     /**
506      * Adjusts the blocking mode.
507      */
508     private void lockedConfigureBlocking(boolean block) throws IOException {
509         assert acceptLock.isHeldByCurrentThread();
510         synchronized (stateLock) {
511             ensureOpen();
512             // do nothing if virtual thread has forced the socket to be non-blocking
513             if (!forcedNonBlocking) {
514                 IOUtil.configureBlocking(fd, block);
515             }
516         }
517     }
518 
519     /**
520      * Attempts to adjust the blocking mode if the channel is open.
521      * @return {@code true} if the blocking mode was adjusted
522      */
523     private boolean tryLockedConfigureBlocking(boolean block) throws IOException {
524         assert acceptLock.isHeldByCurrentThread();
525         synchronized (stateLock) {
526             // do nothing if virtual thread has forced the socket to be non-blocking
527             if (!forcedNonBlocking && isOpen()) {
528                 IOUtil.configureBlocking(fd, block);
529                 return true;
530             } else {
531                 return false;
532             }
533         }
534     }
535 
536     /**
537      * Ensures that the socket is configured non-blocking when on a virtual thread.
538      */
539     private void configureSocketNonBlockingIfVirtualThread() throws IOException {
540         assert acceptLock.isHeldByCurrentThread();
541         if (!forcedNonBlocking && Thread.currentThread().isVirtual()) {
542             synchronized (stateLock) {
543                 ensureOpen();
544                 IOUtil.configureBlocking(fd, false);
545                 forcedNonBlocking = true;
546             }
547         }
548     }
549 
550     /**
551      * Closes the socket if there are no accept in progress and the channel is
552      * not registered with a Selector.
553      */
554     private boolean tryClose() throws IOException {
555         assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
556         if ((thread == null) && !isRegistered()) {
557             state = ST_CLOSED;
558             nd.close(fd);
559             return true;
560         } else {
561             return false;
562         }
563     }
564 
565     /**
566      * Invokes tryClose to attempt to close the socket.
567      *
568      * This method is used for deferred closing by I/O and Selector operations.
569      */
570     private void tryFinishClose() {
571         try {
572             tryClose();
573         } catch (IOException ignore) { }
574     }
575 
576     /**
577      * Closes this channel when configured in blocking mode.
578      *
579      * If there is an accept in progress then the socket is pre-closed and the
580      * accept thread is signalled, in which case the final close is deferred
581      * until the accept aborts.
582      */
583     private void implCloseBlockingMode() throws IOException {
584         synchronized (stateLock) {
585             assert state < ST_CLOSING;
586             state = ST_CLOSING;
587             if (!tryClose()) {
588                 nd.preClose(fd, thread, null);
589             }
590         }
591     }
592 
593     /**
594      * Closes this channel when configured in non-blocking mode.
595      *
596      * If the channel is registered with a Selector then the close is deferred
597      * until the channel is flushed from all Selectors.
598      */
599     private void implCloseNonBlockingMode() throws IOException {
600         synchronized (stateLock) {
601             assert state < ST_CLOSING;
602             state = ST_CLOSING;
603         }
604         // wait for any accept to complete before trying to close
605         acceptLock.lock();
606         acceptLock.unlock();
607         synchronized (stateLock) {
608             if (state == ST_CLOSING) {
609                 tryClose();
610             }
611         }
612     }
613 
614     /**
615      * Invoked by implCloseChannel to close the channel.
616      */
617     @Override
618     protected void implCloseSelectableChannel() throws IOException {
619         assert !isOpen();
620         if (isBlocking()) {
621             implCloseBlockingMode();
622         } else {
623             implCloseNonBlockingMode();
624         }
625     }
626 
627     @Override
628     public void kill() {
629         // wait for any accept operation to complete before trying to close
630         acceptLock.lock();
631         acceptLock.unlock();
632         synchronized (stateLock) {
633             if (state == ST_CLOSING) {
634                 tryFinishClose();
635             }
636         }
637     }
638 
639     /**
640      * Returns true if channel's socket is bound
641      */
642     boolean isBound() {
643         synchronized (stateLock) {
644             return localAddress != null;
645         }
646     }
647 
648     /**
649      * Returns the local address, or null if not bound
650      */
651     SocketAddress localAddress() {
652         synchronized (stateLock) {
653             return localAddress;
654         }
655     }
656 
657     /**
658      * Translates native poll revent set into a ready operation set
659      */
660     public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
661         int intOps = ski.nioInterestOps();
662         int oldOps = ski.nioReadyOps();
663         int newOps = initialOps;
664 
665         if ((ops & Net.POLLNVAL) != 0) {
666             // This should only happen if this channel is pre-closed while a
667             // selection operation is in progress
668             // ## Throw an error if this channel has not been pre-closed
669             return false;
670         }
671 
672         if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
673             newOps = intOps;
674             ski.nioReadyOps(newOps);
675             return (newOps & ~oldOps) != 0;
676         }
677 
678         if (((ops & Net.POLLIN) != 0) &&
679             ((intOps & SelectionKey.OP_ACCEPT) != 0))
680                 newOps |= SelectionKey.OP_ACCEPT;
681 
682         ski.nioReadyOps(newOps);
683         return (newOps & ~oldOps) != 0;
684     }
685 
686     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
687         return translateReadyOps(ops, ski.nioReadyOps(), ski);
688     }
689 
690     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
691         return translateReadyOps(ops, 0, ski);
692     }
693 
694     /**
695      * Translates an interest operation set into a native poll event set
696      */
697     public int translateInterestOps(int ops) {
698         int newOps = 0;
699         if ((ops & SelectionKey.OP_ACCEPT) != 0)
700             newOps |= Net.POLLIN;
701         return newOps;
702     }
703 
704     public FileDescriptor getFD() {
705         return fd;
706     }
707 
708     public int getFDVal() {
709         return fdVal;
710     }
711 
712     public String toString() {
713         StringBuilder sb = new StringBuilder();
714         sb.append(this.getClass().getName());
715         sb.append('[');
716         if (!isOpen()) {
717             sb.append("closed");
718         } else {
719             synchronized (stateLock) {
720                 SocketAddress addr = localAddress;
721                 if (addr == null) {
722                     sb.append("unbound");
723                 } else if (isUnixSocket()) {
724                     sb.append(addr);
725                 }
726             }
727         }
728         sb.append(']');
729         return sb.toString();
730     }
731 }