Path: blob/master/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
41152 views
/*1* Copyright (c) 1995, 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*/2425package java.net;2627import java.io.FileDescriptor;28import java.io.IOException;29import java.io.InputStream;30import java.io.OutputStream;3132import java.security.AccessController;33import java.security.PrivilegedActionException;34import java.security.PrivilegedExceptionAction;35import java.util.Collections;36import java.util.HashSet;37import java.util.Objects;38import java.util.Set;3940import sun.net.ConnectionResetException;41import sun.net.NetHooks;42import sun.net.PlatformSocketImpl;43import sun.net.ResourceManager;44import sun.net.ext.ExtendedSocketOptions;45import sun.net.util.IPAddressUtil;46import sun.net.util.SocketExceptions;4748/**49* Default Socket Implementation. This implementation does50* not implement any security checks.51* Note this class should <b>NOT</b> be public.52*53* @author Steven B. Byrne54*/55abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSocketImpl {56/* instance variable for SO_TIMEOUT */57int timeout; // timeout in millisec58// traffic class59private int trafficClass;6061private boolean shut_rd = false;62private boolean shut_wr = false;6364private SocketInputStream socketInputStream = null;65private SocketOutputStream socketOutputStream = null;6667/* number of threads using the FileDescriptor */68protected int fdUseCount = 0;6970/* lock when increment/decrementing fdUseCount */71protected final Object fdLock = new Object();7273/* indicates a close is pending on the file descriptor */74protected boolean closePending = false;7576/* indicates connection reset state */77private volatile boolean connectionReset;7879/* indicates whether impl is bound */80boolean isBound;8182/* indicates whether impl is connected */83volatile boolean isConnected;8485/* whether this Socket is a stream (TCP) socket or not (UDP)86*/87protected boolean stream;8889/* whether this is a server or not */90final boolean isServer;9192/**93* Load net library into runtime.94*/95static {96jdk.internal.loader.BootLoader.loadLibrary("net");97}9899private static volatile boolean checkedReusePort;100private static volatile boolean isReusePortAvailable;101102/**103* Tells whether SO_REUSEPORT is supported.104*/105static boolean isReusePortAvailable() {106if (!checkedReusePort) {107isReusePortAvailable = isReusePortAvailable0();108checkedReusePort = true;109}110return isReusePortAvailable;111}112113AbstractPlainSocketImpl(boolean isServer) {114this.isServer = isServer;115}116117/**118* Creates a socket with a boolean that specifies whether this119* is a stream socket (true) or an unconnected UDP socket (false).120*/121protected synchronized void create(boolean stream) throws IOException {122this.stream = stream;123if (!stream) {124ResourceManager.beforeUdpCreate();125// only create the fd after we know we will be able to create the socket126fd = new FileDescriptor();127try {128socketCreate(false);129SocketCleanable.register(fd, false);130} catch (IOException ioe) {131ResourceManager.afterUdpClose();132fd = null;133throw ioe;134}135} else {136fd = new FileDescriptor();137socketCreate(true);138SocketCleanable.register(fd, true);139}140}141142/**143* Creates a socket and connects it to the specified port on144* the specified host.145* @param host the specified host146* @param port the specified port147*/148protected void connect(String host, int port)149throws UnknownHostException, IOException150{151boolean connected = false;152try {153InetAddress address = InetAddress.getByName(host);154// recording this.address as supplied by caller before calling connect155this.address = address;156this.port = port;157if (address.isLinkLocalAddress()) {158address = IPAddressUtil.toScopedAddress(address);159}160161connectToAddress(address, port, timeout);162connected = true;163} finally {164if (!connected) {165try {166close();167} catch (IOException ioe) {168/* Do nothing. If connect threw an exception then169it will be passed up the call stack */170}171}172isConnected = connected;173}174}175176/**177* Creates a socket and connects it to the specified address on178* the specified port.179* @param address the address180* @param port the specified port181*/182protected void connect(InetAddress address, int port) throws IOException {183// recording this.address as supplied by caller before calling connect184this.address = address;185this.port = port;186if (address.isLinkLocalAddress()) {187address = IPAddressUtil.toScopedAddress(address);188}189190try {191connectToAddress(address, port, timeout);192isConnected = true;193return;194} catch (IOException e) {195// everything failed196close();197throw e;198}199}200201/**202* Creates a socket and connects it to the specified address on203* the specified port.204* @param address the address205* @param timeout the timeout value in milliseconds, or zero for no timeout.206* @throws IOException if connection fails207* @throws IllegalArgumentException if address is null or is a208* SocketAddress subclass not supported by this socket209* @since 1.4210*/211protected void connect(SocketAddress address, int timeout)212throws IOException {213boolean connected = false;214try {215if (!(address instanceof InetSocketAddress addr))216throw new IllegalArgumentException("unsupported address type");217if (addr.isUnresolved())218throw new UnknownHostException(addr.getHostName());219// recording this.address as supplied by caller before calling connect220InetAddress ia = addr.getAddress();221this.address = ia;222this.port = addr.getPort();223if (ia.isLinkLocalAddress()) {224ia = IPAddressUtil.toScopedAddress(ia);225}226connectToAddress(ia, port, timeout);227connected = true;228} finally {229if (!connected) {230try {231close();232} catch (IOException ioe) {233/* Do nothing. If connect threw an exception then234it will be passed up the call stack */235}236}237isConnected = connected;238}239}240241private void connectToAddress(InetAddress address, int port, int timeout) throws IOException {242if (address.isAnyLocalAddress()) {243doConnect(InetAddress.getLocalHost(), port, timeout);244} else {245doConnect(address, port, timeout);246}247}248249public void setOption(int opt, Object val) throws SocketException {250if (isClosedOrPending()) {251throw new SocketException("Socket Closed");252}253boolean on = true;254switch (opt) {255/* check type safety b4 going native. These should never256* fail, since only java.Socket* has access to257* PlainSocketImpl.setOption().258*/259case SO_LINGER:260if (!(val instanceof Integer) && !(val instanceof Boolean))261throw new SocketException("Bad parameter for option");262if (val instanceof Boolean) {263/* true only if disabling - enabling should be Integer */264on = false;265}266break;267case SO_TIMEOUT:268if (!(val instanceof Integer))269throw new SocketException("Bad parameter for SO_TIMEOUT");270int tmp = ((Integer) val).intValue();271if (tmp < 0)272throw new IllegalArgumentException("timeout < 0");273timeout = tmp;274break;275case IP_TOS:276if (!(val instanceof Integer)) {277throw new SocketException("bad argument for IP_TOS");278}279trafficClass = ((Integer)val).intValue();280break;281case SO_BINDADDR:282throw new SocketException("Cannot re-bind socket");283case TCP_NODELAY:284if (!(val instanceof Boolean))285throw new SocketException("bad parameter for TCP_NODELAY");286on = ((Boolean)val).booleanValue();287break;288case SO_SNDBUF:289case SO_RCVBUF:290if (!(val instanceof Integer) ||291!(((Integer)val).intValue() > 0)) {292throw new SocketException("bad parameter for SO_SNDBUF " +293"or SO_RCVBUF");294}295break;296case SO_KEEPALIVE:297if (!(val instanceof Boolean))298throw new SocketException("bad parameter for SO_KEEPALIVE");299on = ((Boolean)val).booleanValue();300break;301case SO_OOBINLINE:302if (!(val instanceof Boolean))303throw new SocketException("bad parameter for SO_OOBINLINE");304on = ((Boolean)val).booleanValue();305break;306case SO_REUSEADDR:307if (!(val instanceof Boolean))308throw new SocketException("bad parameter for SO_REUSEADDR");309on = ((Boolean)val).booleanValue();310break;311case SO_REUSEPORT:312if (!(val instanceof Boolean))313throw new SocketException("bad parameter for SO_REUSEPORT");314if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT))315throw new UnsupportedOperationException("unsupported option");316on = ((Boolean)val).booleanValue();317break;318default:319throw new SocketException("unrecognized TCP option: " + opt);320}321socketSetOption(opt, on, val);322}323public Object getOption(int opt) throws SocketException {324if (isClosedOrPending()) {325throw new SocketException("Socket Closed");326}327if (opt == SO_TIMEOUT) {328return timeout;329}330int ret = 0;331/*332* The native socketGetOption() knows about 3 options.333* The 32 bit value it returns will be interpreted according334* to what we're asking. A return of -1 means it understands335* the option but its turned off. It will raise a SocketException336* if "opt" isn't one it understands.337*/338339switch (opt) {340case TCP_NODELAY:341ret = socketGetOption(opt, null);342return Boolean.valueOf(ret != -1);343case SO_OOBINLINE:344ret = socketGetOption(opt, null);345return Boolean.valueOf(ret != -1);346case SO_LINGER:347ret = socketGetOption(opt, null);348return (ret == -1) ? Boolean.FALSE: (Object)(ret);349case SO_REUSEADDR:350ret = socketGetOption(opt, null);351return Boolean.valueOf(ret != -1);352case SO_BINDADDR:353InetAddressContainer in = new InetAddressContainer();354ret = socketGetOption(opt, in);355return in.addr;356case SO_SNDBUF:357case SO_RCVBUF:358ret = socketGetOption(opt, null);359return ret;360case IP_TOS:361try {362ret = socketGetOption(opt, null);363if (ret == -1) { // ipv6 tos364return trafficClass;365} else {366return ret;367}368} catch (SocketException se) {369// TODO - should make better effort to read TOS or TCLASS370return trafficClass; // ipv6 tos371}372case SO_KEEPALIVE:373ret = socketGetOption(opt, null);374return Boolean.valueOf(ret != -1);375case SO_REUSEPORT:376if (!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {377throw new UnsupportedOperationException("unsupported option");378}379ret = socketGetOption(opt, null);380return Boolean.valueOf(ret != -1);381// should never get here382default:383return null;384}385}386387static final ExtendedSocketOptions extendedOptions =388ExtendedSocketOptions.getInstance();389390private static final Set<SocketOption<?>> clientSocketOptions = clientSocketOptions();391private static final Set<SocketOption<?>> serverSocketOptions = serverSocketOptions();392393private static Set<SocketOption<?>> clientSocketOptions() {394HashSet<SocketOption<?>> options = new HashSet<>();395options.add(StandardSocketOptions.SO_KEEPALIVE);396options.add(StandardSocketOptions.SO_SNDBUF);397options.add(StandardSocketOptions.SO_RCVBUF);398options.add(StandardSocketOptions.SO_REUSEADDR);399options.add(StandardSocketOptions.SO_LINGER);400options.add(StandardSocketOptions.IP_TOS);401options.add(StandardSocketOptions.TCP_NODELAY);402if (isReusePortAvailable())403options.add(StandardSocketOptions.SO_REUSEPORT);404options.addAll(ExtendedSocketOptions.clientSocketOptions());405return Collections.unmodifiableSet(options);406}407408private static Set<SocketOption<?>> serverSocketOptions() {409HashSet<SocketOption<?>> options = new HashSet<>();410options.add(StandardSocketOptions.SO_RCVBUF);411options.add(StandardSocketOptions.SO_REUSEADDR);412options.add(StandardSocketOptions.IP_TOS);413if (isReusePortAvailable())414options.add(StandardSocketOptions.SO_REUSEPORT);415options.addAll(ExtendedSocketOptions.serverSocketOptions());416return Collections.unmodifiableSet(options);417}418419@Override420protected Set<SocketOption<?>> supportedOptions() {421if (isServer)422return serverSocketOptions;423else424return clientSocketOptions;425}426427@Override428protected <T> void setOption(SocketOption<T> name, T value) throws IOException {429Objects.requireNonNull(name);430if (!supportedOptions().contains(name))431throw new UnsupportedOperationException("'" + name + "' not supported");432433if (!name.type().isInstance(value))434throw new IllegalArgumentException("Invalid value '" + value + "'");435436if (isClosedOrPending())437throw new SocketException("Socket closed");438439if (name == StandardSocketOptions.SO_KEEPALIVE) {440setOption(SocketOptions.SO_KEEPALIVE, value);441} else if (name == StandardSocketOptions.SO_SNDBUF) {442if (((Integer)value).intValue() < 0)443throw new IllegalArgumentException("Invalid send buffer size:" + value);444setOption(SocketOptions.SO_SNDBUF, value);445} else if (name == StandardSocketOptions.SO_RCVBUF) {446if (((Integer)value).intValue() < 0)447throw new IllegalArgumentException("Invalid recv buffer size:" + value);448setOption(SocketOptions.SO_RCVBUF, value);449} else if (name == StandardSocketOptions.SO_REUSEADDR) {450setOption(SocketOptions.SO_REUSEADDR, value);451} else if (name == StandardSocketOptions.SO_REUSEPORT) {452setOption(SocketOptions.SO_REUSEPORT, value);453} else if (name == StandardSocketOptions.SO_LINGER ) {454if (((Integer)value).intValue() < 0)455setOption(SocketOptions.SO_LINGER, false);456else457setOption(SocketOptions.SO_LINGER, value);458} else if (name == StandardSocketOptions.IP_TOS) {459int i = ((Integer)value).intValue();460if (i < 0 || i > 255)461throw new IllegalArgumentException("Invalid IP_TOS value: " + value);462setOption(SocketOptions.IP_TOS, value);463} else if (name == StandardSocketOptions.TCP_NODELAY) {464setOption(SocketOptions.TCP_NODELAY, value);465} else if (extendedOptions.isOptionSupported(name)) {466extendedOptions.setOption(fd, name, value);467} else {468throw new AssertionError("unknown option: " + name);469}470}471472@Override473@SuppressWarnings("unchecked")474protected <T> T getOption(SocketOption<T> name) throws IOException {475Objects.requireNonNull(name);476if (!supportedOptions().contains(name))477throw new UnsupportedOperationException("'" + name + "' not supported");478479if (isClosedOrPending())480throw new SocketException("Socket closed");481482if (name == StandardSocketOptions.SO_KEEPALIVE) {483return (T)getOption(SocketOptions.SO_KEEPALIVE);484} else if (name == StandardSocketOptions.SO_SNDBUF) {485return (T)getOption(SocketOptions.SO_SNDBUF);486} else if (name == StandardSocketOptions.SO_RCVBUF) {487return (T)getOption(SocketOptions.SO_RCVBUF);488} else if (name == StandardSocketOptions.SO_REUSEADDR) {489return (T)getOption(SocketOptions.SO_REUSEADDR);490} else if (name == StandardSocketOptions.SO_REUSEPORT) {491return (T)getOption(SocketOptions.SO_REUSEPORT);492} else if (name == StandardSocketOptions.SO_LINGER) {493Object value = getOption(SocketOptions.SO_LINGER);494if (value instanceof Boolean) {495assert ((Boolean)value).booleanValue() == false;496value = -1;497}498return (T)value;499} else if (name == StandardSocketOptions.IP_TOS) {500return (T)getOption(SocketOptions.IP_TOS);501} else if (name == StandardSocketOptions.TCP_NODELAY) {502return (T)getOption(SocketOptions.TCP_NODELAY);503} else if (extendedOptions.isOptionSupported(name)) {504return (T) extendedOptions.getOption(fd, name);505} else {506throw new AssertionError("unknown option: " + name);507}508}509510/**511* The workhorse of the connection operation. Tries several times to512* establish a connection to the given <host, port>. If unsuccessful,513* throws an IOException indicating what went wrong.514*/515516synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {517synchronized (fdLock) {518if (!closePending && !isBound) {519NetHooks.beforeTcpConnect(fd, address, port);520}521}522try {523acquireFD();524try {525socketConnect(address, port, timeout);526/* socket may have been closed during poll/select */527synchronized (fdLock) {528if (closePending) {529throw new SocketException ("Socket closed");530}531}532} finally {533releaseFD();534}535} catch (IOException e) {536close();537throw SocketExceptions.of(e, new InetSocketAddress(address, port));538}539}540541/**542* Binds the socket to the specified address of the specified local port.543* @param address the address544* @param lport the port545*/546protected synchronized void bind(InetAddress address, int lport)547throws IOException548{549synchronized (fdLock) {550if (!closePending && !isBound) {551NetHooks.beforeTcpBind(fd, address, lport);552}553}554if (address.isLinkLocalAddress()) {555address = IPAddressUtil.toScopedAddress(address);556}557socketBind(address, lport);558isBound = true;559}560561/**562* Listens, for a specified amount of time, for connections.563* @param count the amount of time to listen for connections564*/565protected synchronized void listen(int count) throws IOException {566socketListen(count);567}568569/**570* Accepts connections.571* @param si the socket impl572*/573protected void accept(SocketImpl si) throws IOException {574si.fd = new FileDescriptor();575acquireFD();576try {577socketAccept(si);578} finally {579releaseFD();580}581SocketCleanable.register(si.fd, true);582}583584/**585* Gets an InputStream for this socket.586*/587@SuppressWarnings("removal")588protected synchronized InputStream getInputStream() throws IOException {589synchronized (fdLock) {590if (isClosedOrPending())591throw new IOException("Socket Closed");592if (shut_rd)593throw new IOException("Socket input is shutdown");594if (socketInputStream == null) {595PrivilegedExceptionAction<SocketInputStream> pa = () -> new SocketInputStream(this);596try {597socketInputStream = AccessController.doPrivileged(pa);598} catch (PrivilegedActionException e) {599throw (IOException) e.getCause();600}601}602}603return socketInputStream;604}605606void setInputStream(SocketInputStream in) {607socketInputStream = in;608}609610/**611* Gets an OutputStream for this socket.612*/613@SuppressWarnings("removal")614protected synchronized OutputStream getOutputStream() throws IOException {615synchronized (fdLock) {616if (isClosedOrPending())617throw new IOException("Socket Closed");618if (shut_wr)619throw new IOException("Socket output is shutdown");620if (socketOutputStream == null) {621PrivilegedExceptionAction<SocketOutputStream> pa = () -> new SocketOutputStream(this);622try {623socketOutputStream = AccessController.doPrivileged(pa);624} catch (PrivilegedActionException e) {625throw (IOException) e.getCause();626}627}628}629return socketOutputStream;630}631632void setFileDescriptor(FileDescriptor fd) {633this.fd = fd;634}635636void setAddress(InetAddress address) {637this.address = address;638}639640void setPort(int port) {641this.port = port;642}643644void setLocalPort(int localport) {645this.localport = localport;646}647648/**649* Returns the number of bytes that can be read without blocking.650*/651protected synchronized int available() throws IOException {652if (isClosedOrPending()) {653throw new IOException("Stream closed.");654}655656/*657* If connection has been reset or shut down for input, then return 0658* to indicate there are no buffered bytes.659*/660if (isConnectionReset() || shut_rd) {661return 0;662}663664/*665* If no bytes available and we were previously notified666* of a connection reset then we move to the reset state.667*668* If are notified of a connection reset then check669* again if there are bytes buffered on the socket.670*/671int n = 0;672try {673n = socketAvailable();674} catch (ConnectionResetException exc1) {675setConnectionReset();676}677return n;678}679680/**681* Closes the socket.682*/683protected void close() throws IOException {684synchronized(fdLock) {685if (fd != null) {686if (fdUseCount == 0) {687if (closePending) {688return;689}690closePending = true;691/*692* We close the FileDescriptor in two-steps - first the693* "pre-close" which closes the socket but doesn't694* release the underlying file descriptor. This operation695* may be lengthy due to untransmitted data and a long696* linger interval. Once the pre-close is done we do the697* actual socket to release the fd.698*/699try {700socketPreClose();701} finally {702socketClose();703}704fd = null;705return;706} else {707/*708* If a thread has acquired the fd and a close709* isn't pending then use a deferred close.710* Also decrement fdUseCount to signal the last711* thread that releases the fd to close it.712*/713if (!closePending) {714closePending = true;715fdUseCount--;716socketPreClose();717}718}719}720}721}722723void reset() {724throw new InternalError("should not get here");725}726727/**728* Shutdown read-half of the socket connection;729*/730protected void shutdownInput() throws IOException {731if (fd != null) {732socketShutdown(SHUT_RD);733if (socketInputStream != null) {734socketInputStream.setEOF(true);735}736shut_rd = true;737}738}739740/**741* Shutdown write-half of the socket connection;742*/743protected void shutdownOutput() throws IOException {744if (fd != null) {745socketShutdown(SHUT_WR);746shut_wr = true;747}748}749750protected boolean supportsUrgentData () {751return true;752}753754protected void sendUrgentData (int data) throws IOException {755if (fd == null) {756throw new IOException("Socket Closed");757}758socketSendUrgentData (data);759}760761/*762* "Acquires" and returns the FileDescriptor for this impl763*764* A corresponding releaseFD is required to "release" the765* FileDescriptor.766*/767FileDescriptor acquireFD() {768synchronized (fdLock) {769fdUseCount++;770return fd;771}772}773774/*775* "Release" the FileDescriptor for this impl.776*777* If the use count goes to -1 then the socket is closed.778*/779void releaseFD() {780synchronized (fdLock) {781fdUseCount--;782if (fdUseCount == -1) {783if (fd != null) {784try {785socketClose();786} catch (IOException e) {787} finally {788fd = null;789}790}791}792}793}794795boolean isConnectionReset() {796return connectionReset;797}798799void setConnectionReset() {800connectionReset = true;801}802803/*804* Return true if already closed or close is pending805*/806public boolean isClosedOrPending() {807/*808* Lock on fdLock to ensure that we wait if a809* close is in progress.810*/811synchronized (fdLock) {812if (closePending || (fd == null)) {813return true;814} else {815return false;816}817}818}819820/*821* Return the current value of SO_TIMEOUT822*/823public int getTimeout() {824return timeout;825}826827/*828* "Pre-close" a socket by dup'ing the file descriptor - this enables829* the socket to be closed without releasing the file descriptor.830*/831private void socketPreClose() throws IOException {832socketClose0(true);833}834835/*836* Close the socket (and release the file descriptor).837*/838protected void socketClose() throws IOException {839SocketCleanable.unregister(fd);840try {841socketClose0(false);842} finally {843if (!stream) {844ResourceManager.afterUdpClose();845}846}847}848849abstract void socketCreate(boolean stream) throws IOException;850abstract void socketConnect(InetAddress address, int port, int timeout)851throws IOException;852abstract void socketBind(InetAddress address, int port)853throws IOException;854abstract void socketListen(int count)855throws IOException;856abstract void socketAccept(SocketImpl s)857throws IOException;858abstract int socketAvailable()859throws IOException;860abstract void socketClose0(boolean useDeferredClose)861throws IOException;862abstract void socketShutdown(int howto)863throws IOException;864abstract void socketSetOption(int cmd, boolean on, Object value)865throws SocketException;866abstract int socketGetOption(int opt, Object iaContainerObj) throws SocketException;867abstract void socketSendUrgentData(int data)868throws IOException;869870public static final int SHUT_RD = 0;871public static final int SHUT_WR = 1;872873private static native boolean isReusePortAvailable0();874}875876877