1/*
2 * Copyright (c) 2007, 2016, 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 */
25package java.net;
26
27import java.io.IOException;
28import java.io.FileDescriptor;
29import jdk.internal.misc.SharedSecrets;
30import jdk.internal.misc.JavaIOFileDescriptorAccess;
31
32/**
33 * This class defines the plain SocketImpl that is used on Windows platforms
34 * greater or equal to Windows Vista. These platforms have a dual
35 * layer TCP/IP stack and can handle both IPv4 and IPV6 through a
36 * single file descriptor.
37 *
38 * @author Chris Hegarty
39 */
40
41class DualStackPlainSocketImpl extends AbstractPlainSocketImpl
42{
43    static JavaIOFileDescriptorAccess fdAccess = SharedSecrets.getJavaIOFileDescriptorAccess();
44
45
46    // true if this socket is exclusively bound
47    private final boolean exclusiveBind;
48
49    // emulates SO_REUSEADDR when exclusiveBind is true
50    private boolean isReuseAddress;
51
52    public DualStackPlainSocketImpl(boolean exclBind) {
53        exclusiveBind = exclBind;
54    }
55
56    public DualStackPlainSocketImpl(FileDescriptor fd, boolean exclBind) {
57        this.fd = fd;
58        exclusiveBind = exclBind;
59    }
60
61    void socketCreate(boolean stream) throws IOException {
62        if (fd == null)
63            throw new SocketException("Socket closed");
64
65        int newfd = socket0(stream, false /*v6 Only*/);
66
67        fdAccess.set(fd, newfd);
68    }
69
70    void socketConnect(InetAddress address, int port, int timeout)
71        throws IOException {
72        int nativefd = checkAndReturnNativeFD();
73
74        if (address == null)
75            throw new NullPointerException("inet address argument is null.");
76
77        int connectResult;
78        if (timeout <= 0) {
79            connectResult = connect0(nativefd, address, port);
80        } else {
81            configureBlocking(nativefd, false);
82            try {
83                connectResult = connect0(nativefd, address, port);
84                if (connectResult == WOULDBLOCK) {
85                    waitForConnect(nativefd, timeout);
86                }
87            } finally {
88                configureBlocking(nativefd, true);
89            }
90        }
91        /*
92         * We need to set the local port field. If bind was called
93         * previous to the connect (by the client) then localport field
94         * will already be set.
95         */
96        if (localport == 0)
97            localport = localPort0(nativefd);
98    }
99
100    void socketBind(InetAddress address, int port) throws IOException {
101        int nativefd = checkAndReturnNativeFD();
102
103        if (address == null)
104            throw new NullPointerException("inet address argument is null.");
105
106        bind0(nativefd, address, port, exclusiveBind);
107        if (port == 0) {
108            localport = localPort0(nativefd);
109        } else {
110            localport = port;
111        }
112
113        this.address = address;
114    }
115
116    void socketListen(int backlog) throws IOException {
117        int nativefd = checkAndReturnNativeFD();
118
119        listen0(nativefd, backlog);
120    }
121
122    void socketAccept(SocketImpl s) throws IOException {
123        int nativefd = checkAndReturnNativeFD();
124
125        if (s == null)
126            throw new NullPointerException("socket is null");
127
128        int newfd = -1;
129        InetSocketAddress[] isaa = new InetSocketAddress[1];
130        if (timeout <= 0) {
131            newfd = accept0(nativefd, isaa);
132        } else {
133            configureBlocking(nativefd, false);
134            try {
135                waitForNewConnection(nativefd, timeout);
136                newfd = accept0(nativefd, isaa);
137                if (newfd != -1) {
138                    configureBlocking(newfd, true);
139                }
140            } finally {
141                configureBlocking(nativefd, true);
142            }
143        }
144        /* Update (SocketImpl)s' fd */
145        fdAccess.set(s.fd, newfd);
146        /* Update socketImpls remote port, address and localport */
147        InetSocketAddress isa = isaa[0];
148        s.port = isa.getPort();
149        s.address = isa.getAddress();
150        s.localport = localport;
151    }
152
153    int socketAvailable() throws IOException {
154        int nativefd = checkAndReturnNativeFD();
155        return available0(nativefd);
156    }
157
158    void socketClose0(boolean useDeferredClose/*unused*/) throws IOException {
159        if (fd == null)
160            throw new SocketException("Socket closed");
161
162        if (!fd.valid())
163            return;
164
165        final int nativefd = fdAccess.get(fd);
166        fdAccess.set(fd, -1);
167        close0(nativefd);
168    }
169
170    void socketShutdown(int howto) throws IOException {
171        int nativefd = checkAndReturnNativeFD();
172        shutdown0(nativefd, howto);
173    }
174
175    // Intentional fallthrough after SO_REUSEADDR
176    @SuppressWarnings("fallthrough")
177    void socketSetOption(int opt, boolean on, Object value)
178        throws SocketException {
179        int nativefd = checkAndReturnNativeFD();
180
181        if (opt == SO_TIMEOUT) {  // timeout implemented through select.
182            return;
183        }
184        // SO_REUSEPORT is not supported on Windows.
185        if (opt == SO_REUSEPORT) {
186            throw new UnsupportedOperationException("unsupported option");
187        }
188
189        int optionValue = 0;
190
191        switch(opt) {
192            case SO_REUSEADDR :
193                if (exclusiveBind) {
194                    // SO_REUSEADDR emulated when using exclusive bind
195                    isReuseAddress = on;
196                    return;
197                }
198                // intentional fallthrough
199            case TCP_NODELAY :
200            case SO_OOBINLINE :
201            case SO_KEEPALIVE :
202                optionValue = on ? 1 : 0;
203                break;
204            case SO_SNDBUF :
205            case SO_RCVBUF :
206            case IP_TOS :
207                optionValue = ((Integer)value).intValue();
208                break;
209            case SO_LINGER :
210                if (on) {
211                    optionValue =  ((Integer)value).intValue();
212                } else {
213                    optionValue = -1;
214                }
215                break;
216            default :/* shouldn't get here */
217                throw new SocketException("Option not supported");
218        }
219
220        setIntOption(nativefd, opt, optionValue);
221    }
222
223    int socketGetOption(int opt, Object iaContainerObj) throws SocketException {
224        int nativefd = checkAndReturnNativeFD();
225
226        // SO_BINDADDR is not a socket option.
227        if (opt == SO_BINDADDR) {
228            localAddress(nativefd, (InetAddressContainer)iaContainerObj);
229            return 0;  // return value doesn't matter.
230        }
231        // SO_REUSEPORT is not supported on Windows.
232        if (opt == SO_REUSEPORT) {
233            throw new UnsupportedOperationException("unsupported option");
234        }
235
236        // SO_REUSEADDR emulated when using exclusive bind
237        if (opt == SO_REUSEADDR && exclusiveBind)
238            return isReuseAddress? 1 : -1;
239
240        int value = getIntOption(nativefd, opt);
241
242        switch (opt) {
243            case TCP_NODELAY :
244            case SO_OOBINLINE :
245            case SO_KEEPALIVE :
246            case SO_REUSEADDR :
247                return (value == 0) ? -1 : 1;
248        }
249        return value;
250    }
251
252    void socketSendUrgentData(int data) throws IOException {
253        int nativefd = checkAndReturnNativeFD();
254        sendOOB(nativefd, data);
255    }
256
257    private int checkAndReturnNativeFD() throws SocketException {
258        if (fd == null || !fd.valid())
259            throw new SocketException("Socket closed");
260
261        return fdAccess.get(fd);
262    }
263
264    static final int WOULDBLOCK = -2;       // Nothing available (non-blocking)
265
266    static {
267        initIDs();
268    }
269
270    /* Native methods */
271
272    static native void initIDs();
273
274    static native int socket0(boolean stream, boolean v6Only) throws IOException;
275
276    static native void bind0(int fd, InetAddress localAddress, int localport,
277                             boolean exclBind)
278        throws IOException;
279
280    static native int connect0(int fd, InetAddress remote, int remotePort)
281        throws IOException;
282
283    static native void waitForConnect(int fd, int timeout) throws IOException;
284
285    static native int localPort0(int fd) throws IOException;
286
287    static native void localAddress(int fd, InetAddressContainer in) throws SocketException;
288
289    static native void listen0(int fd, int backlog) throws IOException;
290
291    static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException;
292
293    static native void waitForNewConnection(int fd, int timeout) throws IOException;
294
295    static native int available0(int fd) throws IOException;
296
297    static native void close0(int fd) throws IOException;
298
299    static native void shutdown0(int fd, int howto) throws IOException;
300
301    static native void setIntOption(int fd, int cmd, int optionValue) throws SocketException;
302
303    static native int getIntOption(int fd, int cmd) throws SocketException;
304
305    static native void sendOOB(int fd, int data) throws IOException;
306
307    static native void configureBlocking(int fd, boolean blocking) throws IOException;
308}
309