OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / harmony / luni / net / PlainSocketImpl.java
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 package org.apache.harmony.luni.net;
19
20 import java.io.FileDescriptor;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.lang.reflect.Field;
25 import java.net.ConnectException;
26 import java.net.InetAddress;
27 import java.net.InetSocketAddress;
28 import java.net.Proxy;
29 import java.net.SocketAddress;
30 import java.net.SocketException;
31 import java.net.SocketImpl;
32 import java.net.SocketTimeoutException;
33 import java.net.UnknownHostException;
34 import java.security.AccessController;
35 import java.security.PrivilegedAction;
36 import org.apache.harmony.luni.platform.INetworkSystem;
37 import org.apache.harmony.luni.platform.Platform;
38
39 /**
40  * A concrete connected-socket implementation.
41  */
42 public class PlainSocketImpl extends SocketImpl {
43
44     // For SOCKS support. A SOCKS bind() uses the last
45     // host connected to in its request.
46     static private InetAddress lastConnectedAddress;
47
48     static private int lastConnectedPort;
49
50     private static Field fdField;
51
52     protected INetworkSystem netImpl = Platform.getNetworkSystem();
53
54     private boolean streaming = true;
55
56     private boolean shutdownInput;
57
58     private Proxy proxy;
59
60     public PlainSocketImpl(FileDescriptor fd) {
61         this.fd = fd;
62     }
63
64     public PlainSocketImpl(Proxy proxy) {
65         this(new FileDescriptor());
66         this.proxy = proxy;
67     }
68
69     public PlainSocketImpl() {
70         this(new FileDescriptor());
71     }
72
73     public PlainSocketImpl(FileDescriptor fd, int localport, InetAddress addr, int port) {
74         super();
75         this.fd = fd;
76         this.localport = localport;
77         this.address = addr;
78         this.port = port;
79     }
80
81     @Override
82     protected void accept(SocketImpl newImpl) throws IOException {
83         if (usingSocks()) {
84             ((PlainSocketImpl) newImpl).socksBind();
85             ((PlainSocketImpl) newImpl).socksAccept();
86             return;
87         }
88
89         try {
90             if (newImpl instanceof PlainSocketImpl) {
91                 PlainSocketImpl newPlainSocketImpl = (PlainSocketImpl) newImpl;
92                 netImpl.accept(fd, newImpl, newPlainSocketImpl.getFileDescriptor());
93             } else {
94                 // if newImpl is not an instance of PlainSocketImpl, use
95                 // reflection to get/set protected fields.
96                 if (fdField == null) {
97                     fdField = getSocketImplField("fd");
98                 }
99                 FileDescriptor newFd = (FileDescriptor) fdField.get(newImpl);
100                 netImpl.accept(fd, newImpl, newFd);
101             }
102         } catch (IllegalAccessException e) {
103             // empty
104         }
105     }
106
107     private boolean usingSocks() {
108         return proxy != null && proxy.type() == Proxy.Type.SOCKS;
109     }
110
111     /**
112      * gets SocketImpl field by reflection.
113      */
114     private Field getSocketImplField(final String fieldName) {
115         return AccessController.doPrivileged(new PrivilegedAction<Field>() {
116             public Field run() {
117                 Field field = null;
118                 try {
119                     field = SocketImpl.class.getDeclaredField(fieldName);
120                     field.setAccessible(true);
121                 } catch (NoSuchFieldException e) {
122                     throw new Error(e);
123                 }
124                 return field;
125             }
126         });
127     }
128
129     public void initLocalPort(int localPort) {
130         this.localport = localPort;
131     }
132
133     public void initRemoteAddressAndPort(InetAddress remoteAddress, int remotePort) {
134         this.address = remoteAddress;
135         this.port = remotePort;
136     }
137
138     private void checkNotClosed() throws IOException {
139         if (!fd.valid()) {
140             throw new SocketException("Socket is closed");
141         }
142     }
143
144     @Override
145     protected synchronized int available() throws IOException {
146         checkNotClosed();
147         // we need to check if the input has been shutdown. If so
148         // we should return that there is no data to be read
149         if (shutdownInput) {
150             return 0;
151         }
152         return Platform.getFileSystem().ioctlAvailable(fd);
153     }
154
155     @Override
156     protected void bind(InetAddress address, int port) throws IOException {
157         netImpl.bind(fd, address, port);
158         this.address = address;
159         if (port != 0) {
160             this.localport = port;
161         } else {
162             this.localport = netImpl.getSocketLocalPort(fd);
163         }
164     }
165
166     @Override
167     protected void close() throws IOException {
168         synchronized (fd) {
169             if (fd.valid()) {
170                 netImpl.close(fd);
171                 fd = new FileDescriptor();
172             }
173         }
174     }
175
176     @Override
177     protected void connect(String aHost, int aPort) throws IOException {
178         connect(InetAddress.getByName(aHost), aPort);
179     }
180
181     @Override
182     protected void connect(InetAddress anAddr, int aPort) throws IOException {
183         connect(anAddr, aPort, 0);
184     }
185
186     /**
187      * Connects this socket to the specified remote host address/port.
188      *
189      * @param anAddr
190      *            the remote host address to connect to
191      * @param aPort
192      *            the remote port to connect to
193      * @param timeout
194      *            a timeout where supported. 0 means no timeout
195      * @throws IOException
196      *             if an error occurs while connecting
197      */
198     private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException {
199         InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr;
200         try {
201             if (streaming && usingSocks()) {
202                 socksConnect(anAddr, aPort, 0);
203             } else {
204                 netImpl.connect(fd, normalAddr, aPort, timeout);
205             }
206         } catch (ConnectException e) {
207             throw new ConnectException(anAddr + ":" + aPort + " - " + e.getMessage());
208         }
209         super.address = normalAddr;
210         super.port = aPort;
211     }
212
213     @Override
214     protected void create(boolean streaming) throws IOException {
215         this.streaming = streaming;
216         netImpl.socket(fd, streaming);
217     }
218
219     @Override protected void finalize() throws Throwable {
220         try {
221             close();
222         } finally {
223             super.finalize();
224         }
225     }
226
227     @Override
228     protected synchronized InputStream getInputStream() throws IOException {
229         checkNotClosed();
230         return new SocketInputStream(this);
231     }
232
233     @Override
234     public Object getOption(int optID) throws SocketException {
235         return netImpl.getSocketOption(fd, optID);
236     }
237
238     @Override
239     protected synchronized OutputStream getOutputStream() throws IOException {
240         checkNotClosed();
241         return new SocketOutputStream(this);
242     }
243
244     @Override
245     protected void listen(int backlog) throws IOException {
246         if (usingSocks()) {
247             // Do nothing for a SOCKS connection. The listen occurs on the
248             // server during the bind.
249             return;
250         }
251         netImpl.listen(fd, backlog);
252     }
253
254     @Override
255     public void setOption(int optID, Object val) throws SocketException {
256         netImpl.setSocketOption(fd, optID, val);
257     }
258
259     /**
260      * Gets the SOCKS proxy server port.
261      */
262     private int socksGetServerPort() {
263         // get socks server port from proxy. It is unnecessary to check
264         // "socksProxyPort" property, since proxy setting should only be
265         // determined by ProxySelector.
266         InetSocketAddress addr = (InetSocketAddress) proxy.address();
267         return addr.getPort();
268
269     }
270
271     /**
272      * Gets the InetAddress of the SOCKS proxy server.
273      */
274     private InetAddress socksGetServerAddress() throws UnknownHostException {
275         String proxyName;
276         // get socks server address from proxy. It is unnecessary to check
277         // "socksProxyHost" property, since all proxy setting should be
278         // determined by ProxySelector.
279         InetSocketAddress addr = (InetSocketAddress) proxy.address();
280         proxyName = addr.getHostName();
281         if (null == proxyName) {
282             proxyName = addr.getAddress().getHostAddress();
283         }
284         return InetAddress.getByName(proxyName);
285     }
286
287     /**
288      * Connect using a SOCKS server.
289      */
290     private void socksConnect(InetAddress applicationServerAddress,
291             int applicationServerPort, int timeout) throws IOException {
292         try {
293             netImpl.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
294         } catch (Exception e) {
295             throw new SocketException("SOCKS connection failed: " + e);
296         }
297
298         socksRequestConnection(applicationServerAddress, applicationServerPort);
299
300         lastConnectedAddress = applicationServerAddress;
301         lastConnectedPort = applicationServerPort;
302     }
303
304     /**
305      * Request a SOCKS connection to the application server given. If the
306      * request fails to complete successfully, an exception is thrown.
307      */
308     private void socksRequestConnection(InetAddress applicationServerAddress,
309             int applicationServerPort) throws IOException {
310         socksSendRequest(Socks4Message.COMMAND_CONNECT,
311                 applicationServerAddress, applicationServerPort);
312         Socks4Message reply = socksReadReply();
313         if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
314             throw new IOException(reply.getErrorString(reply
315                     .getCommandOrResult()));
316         }
317     }
318
319     /**
320      * Perform an accept for a SOCKS bind.
321      */
322     public void socksAccept() throws IOException {
323         Socks4Message reply = socksReadReply();
324         if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
325             throw new IOException(reply.getErrorString(reply
326                     .getCommandOrResult()));
327         }
328     }
329
330     /**
331      * Shutdown the input portion of the socket.
332      */
333     @Override
334     protected void shutdownInput() throws IOException {
335         shutdownInput = true;
336         netImpl.shutdownInput(fd);
337     }
338
339     /**
340      * Shutdown the output portion of the socket.
341      */
342     @Override
343     protected void shutdownOutput() throws IOException {
344         netImpl.shutdownOutput(fd);
345     }
346
347     /**
348      * Bind using a SOCKS server.
349      */
350     private void socksBind() throws IOException {
351         try {
352             netImpl.connect(fd, socksGetServerAddress(), socksGetServerPort(), 0);
353         } catch (Exception e) {
354             throw new IOException("Unable to connect to SOCKS server: " + e);
355         }
356
357         // There must be a connection to an application host for the bind to
358         // work.
359         if (lastConnectedAddress == null) {
360             throw new SocketException("Invalid SOCKS client");
361         }
362
363         // Use the last connected address and port in the bind request.
364         socksSendRequest(Socks4Message.COMMAND_BIND, lastConnectedAddress,
365                 lastConnectedPort);
366         Socks4Message reply = socksReadReply();
367
368         if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
369             throw new IOException(reply.getErrorString(reply
370                     .getCommandOrResult()));
371         }
372
373         // A peculiarity of socks 4 - if the address returned is 0, use the
374         // original socks server address.
375         if (reply.getIP() == 0) {
376             address = socksGetServerAddress();
377         } else {
378             // IPv6 support not yet required as
379             // currently the Socks4Message.getIP() only returns int,
380             // so only works with IPv4 4byte addresses
381             byte[] replyBytes = new byte[4];
382             intToBytes(reply.getIP(), replyBytes, 0);
383             address = InetAddress.getByAddress(replyBytes);
384         }
385         localport = reply.getPort();
386     }
387
388     private static void intToBytes(int value, byte[] bytes, int start) {
389         /*
390          * Shift the int so the current byte is right-most Use a byte mask of
391          * 255 to single out the last byte.
392          */
393         bytes[start] = (byte) ((value >> 24) & 255);
394         bytes[start + 1] = (byte) ((value >> 16) & 255);
395         bytes[start + 2] = (byte) ((value >> 8) & 255);
396         bytes[start + 3] = (byte) (value & 255);
397     }
398
399     /**
400      * Send a SOCKS V4 request.
401      */
402     private void socksSendRequest(int command, InetAddress address, int port)
403             throws IOException {
404         Socks4Message request = new Socks4Message();
405         request.setCommandOrResult(command);
406         request.setPort(port);
407         request.setIP(address.getAddress());
408         request.setUserId("default");
409
410         getOutputStream().write(request.getBytes(), 0, request.getLength());
411     }
412
413     /**
414      * Read a SOCKS V4 reply.
415      */
416     private Socks4Message socksReadReply() throws IOException {
417         Socks4Message reply = new Socks4Message();
418         int bytesRead = 0;
419         while (bytesRead < Socks4Message.REPLY_LENGTH) {
420             int count = getInputStream().read(reply.getBytes(), bytesRead,
421                     Socks4Message.REPLY_LENGTH - bytesRead);
422             if (-1 == count) {
423                 break;
424             }
425             bytesRead += count;
426         }
427         if (Socks4Message.REPLY_LENGTH != bytesRead) {
428             throw new SocketException("Malformed reply from SOCKS server");
429         }
430         return reply;
431     }
432
433     @Override
434     protected void connect(SocketAddress remoteAddr, int timeout)
435             throws IOException {
436         InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
437         connect(inetAddr.getAddress(), inetAddr.getPort(), timeout);
438     }
439
440     @Override
441     protected boolean supportsUrgentData() {
442         return true;
443     }
444
445     @Override
446     protected void sendUrgentData(int value) throws IOException {
447         netImpl.sendUrgentData(fd, (byte) value);
448     }
449
450     FileDescriptor getFD() {
451         return fd;
452     }
453
454     int read(byte[] buffer, int offset, int count) throws IOException {
455         if (shutdownInput) {
456             return -1;
457         }
458         int read = netImpl.read(fd, buffer, offset, count);
459         // Return of zero bytes for a blocking socket means a timeout occurred
460         if (read == 0) {
461             throw new SocketTimeoutException();
462         }
463         // Return of -1 indicates the peer was closed
464         if (read == -1) {
465             shutdownInput = true;
466         }
467         return read;
468     }
469
470     int write(byte[] buffer, int offset, int count) throws IOException {
471         if (streaming) {
472             return netImpl.write(fd, buffer, offset, count);
473         } else {
474             return netImpl.send(fd, buffer, offset, count, port, address);
475         }
476     }
477 }