1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2000, 2015, 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 
27 package sun.nio.ch;
28 
29 import java.io.*;
30 import java.lang.ref.*;
31 import java.net.*;
32 import java.nio.*;
33 import java.nio.channels.*;
34 import java.security.AccessController;
35 import java.security.PrivilegedExceptionAction;
36 import java.util.*;
37 
38 
39 // Make a socket channel look like a socket.
40 //
41 // The only aspects of java.net.Socket-hood that we don't attempt to emulate
42 // here are the interrupted-I/O exceptions (which our Solaris implementations
43 // attempt to support) and the sending of urgent data.  Otherwise an adapted
44 // socket should look enough like a real java.net.Socket to fool most of the
45 // developers most of the time, right down to the exception message strings.
46 //
47 // The methods in this class are defined in exactly the same order as in
48 // java.net.Socket so as to simplify tracking future changes to that class.
49 //
50 
51 public class SocketAdaptor
52     extends Socket
53 {
54 
55     // The channel being adapted
56     private final SocketChannelImpl sc;
57 
58     // Timeout "option" value for reads
59     private volatile int timeout = 0;
60 
SocketAdaptor(SocketChannelImpl sc)61     private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
62         // Android-changed: Conscrypt compatibility, ensure fd is not null. http://b/25857624
63         super(new FileDescriptorHolderSocketImpl(sc.getFD()));
64         this.sc = sc;
65     }
66 
create(SocketChannelImpl sc)67     public static Socket create(SocketChannelImpl sc) {
68         try {
69             return new SocketAdaptor(sc);
70         } catch (SocketException e) {
71             throw new InternalError("Should not reach here");
72         }
73     }
74 
getChannel()75     public SocketChannel getChannel() {
76         return sc;
77     }
78 
79     // Override this method just to protect against changes in the superclass
80     //
connect(SocketAddress remote)81     public void connect(SocketAddress remote) throws IOException {
82         connect(remote, 0);
83     }
84 
connect(SocketAddress remote, int timeout)85     public void connect(SocketAddress remote, int timeout) throws IOException {
86         if (remote == null)
87             throw new IllegalArgumentException("connect: The address can't be null");
88         if (timeout < 0)
89             throw new IllegalArgumentException("connect: timeout can't be negative");
90 
91         synchronized (sc.blockingLock()) {
92             if (!sc.isBlocking())
93                 throw new IllegalBlockingModeException();
94 
95             try {
96 
97                 if (timeout == 0) {
98                     // Android-changed: Translate exceptions consistently.
99                     // sc.connect(remote);
100                     try {
101                         sc.connect(remote);
102                     } catch (Exception ex) {
103                         Net.translateException(ex);
104                     }
105 
106                     return;
107                 }
108 
109                 sc.configureBlocking(false);
110                 try {
111                     if (sc.connect(remote))
112                         return;
113                     long to = timeout;
114                     for (;;) {
115                         if (!sc.isOpen())
116                             throw new ClosedChannelException();
117                         long st = System.currentTimeMillis();
118 
119                         int result = sc.poll(Net.POLLCONN, to);
120                         if (result > 0 && sc.finishConnect())
121                             break;
122                         to -= System.currentTimeMillis() - st;
123                         if (to <= 0) {
124                             try {
125                                 sc.close();
126                             } catch (IOException x) { }
127                             throw new SocketTimeoutException();
128                         }
129                     }
130                 } finally {
131                     if (sc.isOpen())
132                         sc.configureBlocking(true);
133                 }
134 
135             } catch (Exception x) {
136                 Net.translateException(x, true);
137             }
138         }
139 
140     }
141 
bind(SocketAddress local)142     public void bind(SocketAddress local) throws IOException {
143         try {
144             sc.bind(local);
145         } catch (Exception x) {
146             Net.translateException(x);
147         }
148     }
149 
getInetAddress()150     public InetAddress getInetAddress() {
151         // Android-changed: remoteAddress() returns non-null before connection. http://b/26140820
152         if (!isConnected()) {
153             return null;
154         }
155         SocketAddress remote = sc.remoteAddress();
156         if (remote == null) {
157             return null;
158         } else {
159             return ((InetSocketAddress)remote).getAddress();
160         }
161     }
162 
getLocalAddress()163     public InetAddress getLocalAddress() {
164         if (sc.isOpen()) {
165             InetSocketAddress local = sc.localAddress();
166             if (local != null) {
167                 return Net.getRevealedLocalAddress(local).getAddress();
168             }
169         }
170         return new InetSocketAddress(0).getAddress();
171     }
172 
getPort()173     public int getPort() {
174         // Android-changed: remoteAddress() returns non-null before connection. http://b/26140820
175         if (!isConnected()) {
176           return 0;
177         }
178         SocketAddress remote = sc.remoteAddress();
179         if (remote == null) {
180             return 0;
181         } else {
182             return ((InetSocketAddress)remote).getPort();
183         }
184     }
185 
getLocalPort()186     public int getLocalPort() {
187         SocketAddress local = sc.localAddress();
188         if (local == null) {
189             return -1;
190         } else {
191             return ((InetSocketAddress)local).getPort();
192         }
193     }
194 
195     private class SocketInputStream
196         extends ChannelInputStream
197     {
SocketInputStream()198         private SocketInputStream() {
199             super(sc);
200         }
201 
read(ByteBuffer bb)202         protected int read(ByteBuffer bb)
203             throws IOException
204         {
205             synchronized (sc.blockingLock()) {
206                 if (!sc.isBlocking())
207                     throw new IllegalBlockingModeException();
208                 if (timeout == 0)
209                     return sc.read(bb);
210                 sc.configureBlocking(false);
211 
212                 try {
213                     int n;
214                     if ((n = sc.read(bb)) != 0)
215                         return n;
216                     long to = timeout;
217                     for (;;) {
218                         if (!sc.isOpen())
219                             throw new ClosedChannelException();
220                         long st = System.currentTimeMillis();
221                         int result = sc.poll(Net.POLLIN, to);
222                         if (result > 0) {
223                             if ((n = sc.read(bb)) != 0)
224                                 return n;
225                         }
226                         to -= System.currentTimeMillis() - st;
227                         if (to <= 0)
228                             throw new SocketTimeoutException();
229                     }
230                 } finally {
231                     if (sc.isOpen())
232                         sc.configureBlocking(true);
233                 }
234 
235             }
236         }
237     }
238 
239     private InputStream socketInputStream = null;
240 
getInputStream()241     public InputStream getInputStream() throws IOException {
242         if (!sc.isOpen())
243             throw new SocketException("Socket is closed");
244         if (!sc.isConnected())
245             throw new SocketException("Socket is not connected");
246         if (!sc.isInputOpen())
247             throw new SocketException("Socket input is shutdown");
248         if (socketInputStream == null) {
249             try {
250                 socketInputStream = AccessController.doPrivileged(
251                     new PrivilegedExceptionAction<InputStream>() {
252                         public InputStream run() throws IOException {
253                             return new SocketInputStream();
254                         }
255                     });
256             } catch (java.security.PrivilegedActionException e) {
257                 throw (IOException)e.getException();
258             }
259         }
260         return socketInputStream;
261     }
262 
getOutputStream()263     public OutputStream getOutputStream() throws IOException {
264         if (!sc.isOpen())
265             throw new SocketException("Socket is closed");
266         if (!sc.isConnected())
267             throw new SocketException("Socket is not connected");
268         if (!sc.isOutputOpen())
269             throw new SocketException("Socket output is shutdown");
270         OutputStream os = null;
271         try {
272             os = AccessController.doPrivileged(
273                 new PrivilegedExceptionAction<OutputStream>() {
274                     public OutputStream run() throws IOException {
275                         return Channels.newOutputStream(sc);
276                     }
277                 });
278         } catch (java.security.PrivilegedActionException e) {
279             throw (IOException)e.getException();
280         }
281         return os;
282     }
283 
setBooleanOption(SocketOption<Boolean> name, boolean value)284     private void setBooleanOption(SocketOption<Boolean> name, boolean value)
285         throws SocketException
286     {
287         try {
288             sc.setOption(name, value);
289         } catch (IOException x) {
290             Net.translateToSocketException(x);
291         }
292     }
293 
setIntOption(SocketOption<Integer> name, int value)294     private void setIntOption(SocketOption<Integer> name, int value)
295         throws SocketException
296     {
297         try {
298             sc.setOption(name, value);
299         } catch (IOException x) {
300             Net.translateToSocketException(x);
301         }
302     }
303 
getBooleanOption(SocketOption<Boolean> name)304     private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
305         try {
306             return sc.getOption(name).booleanValue();
307         } catch (IOException x) {
308             Net.translateToSocketException(x);
309             return false;       // keep compiler happy
310         }
311     }
312 
getIntOption(SocketOption<Integer> name)313     private int getIntOption(SocketOption<Integer> name) throws SocketException {
314         try {
315             return sc.getOption(name).intValue();
316         } catch (IOException x) {
317             Net.translateToSocketException(x);
318             return -1;          // keep compiler happy
319         }
320     }
321 
setTcpNoDelay(boolean on)322     public void setTcpNoDelay(boolean on) throws SocketException {
323         setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
324     }
325 
getTcpNoDelay()326     public boolean getTcpNoDelay() throws SocketException {
327         return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
328     }
329 
setSoLinger(boolean on, int linger)330     public void setSoLinger(boolean on, int linger) throws SocketException {
331         if (!on)
332             linger = -1;
333         setIntOption(StandardSocketOptions.SO_LINGER, linger);
334     }
335 
getSoLinger()336     public int getSoLinger() throws SocketException {
337         return getIntOption(StandardSocketOptions.SO_LINGER);
338     }
339 
sendUrgentData(int data)340     public void sendUrgentData(int data) throws IOException {
341         int n = sc.sendOutOfBandData((byte) data);
342         if (n == 0)
343             throw new IOException("Socket buffer full");
344     }
345 
setOOBInline(boolean on)346     public void setOOBInline(boolean on) throws SocketException {
347         setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
348     }
349 
getOOBInline()350     public boolean getOOBInline() throws SocketException {
351         return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
352     }
353 
setSoTimeout(int timeout)354     public void setSoTimeout(int timeout) throws SocketException {
355         if (timeout < 0)
356             throw new IllegalArgumentException("timeout can't be negative");
357         this.timeout = timeout;
358     }
359 
getSoTimeout()360     public int getSoTimeout() throws SocketException {
361         return timeout;
362     }
363 
setSendBufferSize(int size)364     public void setSendBufferSize(int size) throws SocketException {
365         // size 0 valid for SocketChannel, invalid for Socket
366         if (size <= 0)
367             throw new IllegalArgumentException("Invalid send size");
368         setIntOption(StandardSocketOptions.SO_SNDBUF, size);
369     }
370 
getSendBufferSize()371     public int getSendBufferSize() throws SocketException {
372         return getIntOption(StandardSocketOptions.SO_SNDBUF);
373     }
374 
setReceiveBufferSize(int size)375     public void setReceiveBufferSize(int size) throws SocketException {
376         // size 0 valid for SocketChannel, invalid for Socket
377         if (size <= 0)
378             throw new IllegalArgumentException("Invalid receive size");
379         setIntOption(StandardSocketOptions.SO_RCVBUF, size);
380     }
381 
getReceiveBufferSize()382     public int getReceiveBufferSize() throws SocketException {
383         return getIntOption(StandardSocketOptions.SO_RCVBUF);
384     }
385 
setKeepAlive(boolean on)386     public void setKeepAlive(boolean on) throws SocketException {
387         setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
388     }
389 
getKeepAlive()390     public boolean getKeepAlive() throws SocketException {
391         return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
392     }
393 
setTrafficClass(int tc)394     public void setTrafficClass(int tc) throws SocketException {
395         setIntOption(StandardSocketOptions.IP_TOS, tc);
396     }
397 
getTrafficClass()398     public int getTrafficClass() throws SocketException {
399         return getIntOption(StandardSocketOptions.IP_TOS);
400     }
401 
setReuseAddress(boolean on)402     public void setReuseAddress(boolean on) throws SocketException {
403         setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
404     }
405 
getReuseAddress()406     public boolean getReuseAddress() throws SocketException {
407         return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
408     }
409 
close()410     public void close() throws IOException {
411         sc.close();
412     }
413 
shutdownInput()414     public void shutdownInput() throws IOException {
415         try {
416             sc.shutdownInput();
417         } catch (Exception x) {
418             Net.translateException(x);
419         }
420     }
421 
shutdownOutput()422     public void shutdownOutput() throws IOException {
423         try {
424             sc.shutdownOutput();
425         } catch (Exception x) {
426             Net.translateException(x);
427         }
428     }
429 
toString()430     public String toString() {
431         if (sc.isConnected())
432             return "Socket[addr=" + getInetAddress() +
433                 ",port=" + getPort() +
434                 ",localport=" + getLocalPort() + "]";
435         return "Socket[unconnected]";
436     }
437 
isConnected()438     public boolean isConnected() {
439         return sc.isConnected();
440     }
441 
isBound()442     public boolean isBound() {
443         return sc.localAddress() != null;
444     }
445 
isClosed()446     public boolean isClosed() {
447         return !sc.isOpen();
448     }
449 
isInputShutdown()450     public boolean isInputShutdown() {
451         return !sc.isInputOpen();
452     }
453 
isOutputShutdown()454     public boolean isOutputShutdown() {
455         return !sc.isOutputOpen();
456     }
457 
458     // Android-added: for testing and internal use.
459     @Override
getFileDescriptor$()460     public FileDescriptor getFileDescriptor$() {
461         return sc.getFD();
462     }
463 }
464