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