1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import java.io.*;
30import java.lang.ref.*;
31import java.net.*;
32import java.nio.*;
33import java.nio.channels.*;
34import java.security.AccessController;
35import java.security.PrivilegedExceptionAction;
36import java.util.*;
37
38import sun.misc.IoTrace;
39
40
41// Make a socket channel look like a socket.
42//
43// The only aspects of java.net.Socket-hood that we don't attempt to emulate
44// here are the interrupted-I/O exceptions (which our Solaris implementations
45// attempt to support) and the sending of urgent data.  Otherwise an adapted
46// socket should look enough like a real java.net.Socket to fool most of the
47// developers most of the time, right down to the exception message strings.
48//
49// The methods in this class are defined in exactly the same order as in
50// java.net.Socket so as to simplify tracking future changes to that class.
51//
52
53public class SocketAdaptor
54    extends Socket
55{
56
57    // The channel being adapted
58    private final SocketChannelImpl sc;
59
60    // Timeout "option" value for reads
61    private volatile int timeout = 0;
62
63    private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
64        super(new FileDescriptorHolderSocketImpl(sc.getFD()));
65        this.sc = sc;
66    }
67
68    public static Socket create(SocketChannelImpl sc) {
69        try {
70            return new SocketAdaptor(sc);
71        } catch (SocketException e) {
72            throw new InternalError("Should not reach here");
73        }
74    }
75
76    public SocketChannel getChannel() {
77        return sc;
78    }
79
80    // Override this method just to protect against changes in the superclass
81    //
82    public void connect(SocketAddress remote) throws IOException {
83        connect(remote, 0);
84    }
85
86    public void connect(SocketAddress remote, int timeout) throws IOException {
87        if (remote == null)
88            throw new IllegalArgumentException("connect: The address can't be null");
89        if (timeout < 0)
90            throw new IllegalArgumentException("connect: timeout can't be negative");
91
92        synchronized (sc.blockingLock()) {
93            if (!sc.isBlocking())
94                throw new IllegalBlockingModeException();
95
96            try {
97
98                if (timeout == 0) {
99                    // Android-changed: Be consistent
100                    try {
101                        sc.connect(remote);
102                    } catch (Exception ex) {
103                        Net.translateException(ex);
104                    }
105
106                    return;
107                }
108
109                // Implement timeout with a selector
110                SelectionKey sk = null;
111                Selector sel = null;
112                sc.configureBlocking(false);
113                try {
114                    if (sc.connect(remote))
115                        return;
116                    sel = Util.getTemporarySelector(sc);
117                    sk = sc.register(sel, SelectionKey.OP_CONNECT);
118                    long to = timeout;
119                    for (;;) {
120                        if (!sc.isOpen())
121                            throw new ClosedChannelException();
122                        long st = System.currentTimeMillis();
123                        int ns = sel.select(to);
124                        if (ns > 0 &&
125                            sk.isConnectable() && sc.finishConnect())
126                            break;
127                        sel.selectedKeys().remove(sk);
128                        to -= System.currentTimeMillis() - st;
129                        if (to <= 0) {
130                            try {
131                                sc.close();
132                            } catch (IOException x) { }
133                            throw new SocketTimeoutException();
134                        }
135                    }
136                } finally {
137                    if (sk != null)
138                        sk.cancel();
139                    if (sc.isOpen())
140                        sc.configureBlocking(true);
141                    if (sel != null)
142                        Util.releaseTemporarySelector(sel);
143                }
144
145            } catch (Exception x) {
146                Net.translateException(x, true);
147            }
148        }
149
150    }
151
152    public void bind(SocketAddress local) throws IOException {
153        try {
154            sc.bind(local);
155        } catch (Exception x) {
156            Net.translateException(x);
157        }
158    }
159
160    public InetAddress getInetAddress() {
161        // Use #remoteAddress and do manual isConnected check. #getRemoteAddress() returns
162        // non-null result before connection.
163        if (!isConnected()) {
164            return null;
165        }
166        SocketAddress remote = sc.remoteAddress();
167        if (remote == null) {
168            return null;
169        } else {
170            return ((InetSocketAddress)remote).getAddress();
171        }
172    }
173
174    public InetAddress getLocalAddress() {
175        if (sc.isOpen()) {
176            InetSocketAddress local = sc.localAddress();
177            if (local != null)
178                return Net.getRevealedLocalAddress(local).getAddress();
179        }
180        return new InetSocketAddress(0).getAddress();
181    }
182
183    public int getPort() {
184        // Use #remoteAddress and do manual isConnected check. #getRemoteAddress() returns
185        // non-null result before connection.
186        if (!isConnected()) {
187          return 0;
188        }
189        SocketAddress remote = sc.remoteAddress();
190        if (remote == null) {
191            return 0;
192        } else {
193            return ((InetSocketAddress)remote).getPort();
194        }
195    }
196
197    public int getLocalPort() {
198        SocketAddress local = sc.localAddress();
199        if (local == null) {
200            return -1;
201        } else {
202            return ((InetSocketAddress)local).getPort();
203        }
204    }
205
206    private class SocketInputStream
207        extends ChannelInputStream
208    {
209        private SocketInputStream() {
210            super(sc);
211        }
212
213        protected int read(ByteBuffer bb)
214            throws IOException
215        {
216            synchronized (sc.blockingLock()) {
217                if (!sc.isBlocking())
218                    throw new IllegalBlockingModeException();
219                if (timeout == 0)
220                    return sc.read(bb);
221
222                // Implement timeout with a selector
223                SelectionKey sk = null;
224                Selector sel = null;
225                sc.configureBlocking(false);
226                int n = 0;
227                Object traceContext = IoTrace.socketReadBegin();
228                try {
229                    if ((n = sc.read(bb)) != 0)
230                        return n;
231                    sel = Util.getTemporarySelector(sc);
232                    sk = sc.register(sel, SelectionKey.OP_READ);
233                    long to = timeout;
234                    for (;;) {
235                        if (!sc.isOpen())
236                            throw new ClosedChannelException();
237                        long st = System.currentTimeMillis();
238                        int ns = sel.select(to);
239                        if (ns > 0 && sk.isReadable()) {
240                            if ((n = sc.read(bb)) != 0)
241                                return n;
242                        }
243                        sel.selectedKeys().remove(sk);
244                        to -= System.currentTimeMillis() - st;
245                        if (to <= 0)
246                            throw new SocketTimeoutException();
247                    }
248                } finally {
249                    IoTrace.socketReadEnd(traceContext, getInetAddress(),
250                                          getPort(), timeout, n > 0 ? n : 0);
251                    if (sk != null)
252                        sk.cancel();
253                    if (sc.isOpen())
254                        sc.configureBlocking(true);
255                    if (sel != null)
256                        Util.releaseTemporarySelector(sel);
257                }
258
259            }
260        }
261    }
262
263    private InputStream socketInputStream = null;
264
265    public InputStream getInputStream() throws IOException {
266        if (!sc.isOpen())
267            throw new SocketException("Socket is closed");
268        if (!sc.isConnected())
269            throw new SocketException("Socket is not connected");
270        if (!sc.isInputOpen())
271            throw new SocketException("Socket input is shutdown");
272        if (socketInputStream == null) {
273            try {
274                socketInputStream = AccessController.doPrivileged(
275                    new PrivilegedExceptionAction<InputStream>() {
276                        public InputStream run() throws IOException {
277                            return new SocketInputStream();
278                        }
279                    });
280            } catch (java.security.PrivilegedActionException e) {
281                throw (IOException)e.getException();
282            }
283        }
284        return socketInputStream;
285    }
286
287    public OutputStream getOutputStream() throws IOException {
288        if (!sc.isOpen())
289            throw new SocketException("Socket is closed");
290        if (!sc.isConnected())
291            throw new SocketException("Socket is not connected");
292        if (!sc.isOutputOpen())
293            throw new SocketException("Socket output is shutdown");
294        OutputStream os = null;
295        try {
296            os = AccessController.doPrivileged(
297                new PrivilegedExceptionAction<OutputStream>() {
298                    public OutputStream run() throws IOException {
299                        return Channels.newOutputStream(sc);
300                    }
301                });
302        } catch (java.security.PrivilegedActionException e) {
303            throw (IOException)e.getException();
304        }
305        return os;
306    }
307
308    private void setBooleanOption(SocketOption<Boolean> name, boolean value)
309        throws SocketException
310    {
311        try {
312            sc.setOption(name, value);
313        } catch (IOException x) {
314            Net.translateToSocketException(x);
315        }
316    }
317
318    private void setIntOption(SocketOption<Integer> name, int value)
319        throws SocketException
320    {
321        try {
322            sc.setOption(name, value);
323        } catch (IOException x) {
324            Net.translateToSocketException(x);
325        }
326    }
327
328    private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
329        try {
330            return sc.getOption(name).booleanValue();
331        } catch (IOException x) {
332            Net.translateToSocketException(x);
333            return false;       // keep compiler happy
334        }
335    }
336
337    private int getIntOption(SocketOption<Integer> name) throws SocketException {
338        try {
339            return sc.getOption(name).intValue();
340        } catch (IOException x) {
341            Net.translateToSocketException(x);
342            return -1;          // keep compiler happy
343        }
344    }
345
346    public void setTcpNoDelay(boolean on) throws SocketException {
347        setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
348    }
349
350    public boolean getTcpNoDelay() throws SocketException {
351        return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
352    }
353
354    public void setSoLinger(boolean on, int linger) throws SocketException {
355        if (!on)
356            linger = -1;
357        setIntOption(StandardSocketOptions.SO_LINGER, linger);
358    }
359
360    public int getSoLinger() throws SocketException {
361        return getIntOption(StandardSocketOptions.SO_LINGER);
362    }
363
364    public void sendUrgentData(int data) throws IOException {
365        synchronized (sc.blockingLock()) {
366            if (!sc.isBlocking())
367                throw new IllegalBlockingModeException();
368            int n = sc.sendOutOfBandData((byte)data);
369            assert n == 1;
370        }
371    }
372
373    public void setOOBInline(boolean on) throws SocketException {
374        setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
375    }
376
377    public boolean getOOBInline() throws SocketException {
378        return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
379    }
380
381    public void setSoTimeout(int timeout) throws SocketException {
382        if (timeout < 0)
383            throw new IllegalArgumentException("timeout can't be negative");
384        this.timeout = timeout;
385    }
386
387    public int getSoTimeout() throws SocketException {
388        return timeout;
389    }
390
391    public void setSendBufferSize(int size) throws SocketException {
392        // size 0 valid for SocketChannel, invalid for Socket
393        if (size <= 0)
394            throw new IllegalArgumentException("Invalid send size");
395        setIntOption(StandardSocketOptions.SO_SNDBUF, size);
396    }
397
398    public int getSendBufferSize() throws SocketException {
399        return getIntOption(StandardSocketOptions.SO_SNDBUF);
400    }
401
402    public void setReceiveBufferSize(int size) throws SocketException {
403        // size 0 valid for SocketChannel, invalid for Socket
404        if (size <= 0)
405            throw new IllegalArgumentException("Invalid receive size");
406        setIntOption(StandardSocketOptions.SO_RCVBUF, size);
407    }
408
409    public int getReceiveBufferSize() throws SocketException {
410        return getIntOption(StandardSocketOptions.SO_RCVBUF);
411    }
412
413    public void setKeepAlive(boolean on) throws SocketException {
414        setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
415    }
416
417    public boolean getKeepAlive() throws SocketException {
418        return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
419    }
420
421    public void setTrafficClass(int tc) throws SocketException {
422        setIntOption(StandardSocketOptions.IP_TOS, tc);
423    }
424
425    public int getTrafficClass() throws SocketException {
426        return getIntOption(StandardSocketOptions.IP_TOS);
427    }
428
429    public void setReuseAddress(boolean on) throws SocketException {
430        setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
431    }
432
433    public boolean getReuseAddress() throws SocketException {
434        return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
435    }
436
437    public void close() throws IOException {
438        sc.close();
439    }
440
441    public void shutdownInput() throws IOException {
442        try {
443            sc.shutdownInput();
444        } catch (Exception x) {
445            Net.translateException(x);
446        }
447    }
448
449    public void shutdownOutput() throws IOException {
450        try {
451            sc.shutdownOutput();
452        } catch (Exception x) {
453            Net.translateException(x);
454        }
455    }
456
457    public String toString() {
458        if (sc.isConnected())
459            return "Socket[addr=" + getInetAddress() +
460                ",port=" + getPort() +
461                ",localport=" + getLocalPort() + "]";
462        return "Socket[unconnected]";
463    }
464
465    public boolean isConnected() {
466        return sc.isConnected();
467    }
468
469    public boolean isBound() {
470        return sc.localAddress() != null;
471    }
472
473    public boolean isClosed() {
474        return !sc.isOpen();
475    }
476
477    public boolean isInputShutdown() {
478        return !sc.isInputOpen();
479    }
480
481    public boolean isOutputShutdown() {
482        return !sc.isOutputOpen();
483    }
484
485    @Override
486    public FileDescriptor getFileDescriptor$() {
487        return sc.getFD();
488    }
489}
490