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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.harmony.luni.net;
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;
40 * A concrete connected-socket implementation.
42 public class PlainSocketImpl extends SocketImpl {
44 // For SOCKS support. A SOCKS bind() uses the last
45 // host connected to in its request.
46 static private InetAddress lastConnectedAddress;
48 static private int lastConnectedPort;
50 private static Field fdField;
52 protected INetworkSystem netImpl = Platform.getNetworkSystem();
54 private boolean streaming = true;
56 private boolean shutdownInput;
60 public PlainSocketImpl(FileDescriptor fd) {
64 public PlainSocketImpl(Proxy proxy) {
65 this(new FileDescriptor());
69 public PlainSocketImpl() {
70 this(new FileDescriptor());
73 public PlainSocketImpl(FileDescriptor fd, int localport, InetAddress addr, int port) {
76 this.localport = localport;
82 protected void accept(SocketImpl newImpl) throws IOException {
84 ((PlainSocketImpl) newImpl).socksBind();
85 ((PlainSocketImpl) newImpl).socksAccept();
90 if (newImpl instanceof PlainSocketImpl) {
91 PlainSocketImpl newPlainSocketImpl = (PlainSocketImpl) newImpl;
92 netImpl.accept(fd, newImpl, newPlainSocketImpl.getFileDescriptor());
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");
99 FileDescriptor newFd = (FileDescriptor) fdField.get(newImpl);
100 netImpl.accept(fd, newImpl, newFd);
102 } catch (IllegalAccessException e) {
107 private boolean usingSocks() {
108 return proxy != null && proxy.type() == Proxy.Type.SOCKS;
112 * gets SocketImpl field by reflection.
114 private Field getSocketImplField(final String fieldName) {
115 return AccessController.doPrivileged(new PrivilegedAction<Field>() {
119 field = SocketImpl.class.getDeclaredField(fieldName);
120 field.setAccessible(true);
121 } catch (NoSuchFieldException e) {
129 public void initLocalPort(int localPort) {
130 this.localport = localPort;
133 public void initRemoteAddressAndPort(InetAddress remoteAddress, int remotePort) {
134 this.address = remoteAddress;
135 this.port = remotePort;
138 private void checkNotClosed() throws IOException {
140 throw new SocketException("Socket is closed");
145 protected synchronized int available() throws IOException {
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
152 return Platform.getFileSystem().ioctlAvailable(fd);
156 protected void bind(InetAddress address, int port) throws IOException {
157 netImpl.bind(fd, address, port);
158 this.address = address;
160 this.localport = port;
162 this.localport = netImpl.getSocketLocalPort(fd);
167 protected void close() throws IOException {
171 fd = new FileDescriptor();
177 protected void connect(String aHost, int aPort) throws IOException {
178 connect(InetAddress.getByName(aHost), aPort);
182 protected void connect(InetAddress anAddr, int aPort) throws IOException {
183 connect(anAddr, aPort, 0);
187 * Connects this socket to the specified remote host address/port.
190 * the remote host address to connect to
192 * the remote port to connect to
194 * a timeout where supported. 0 means no timeout
195 * @throws IOException
196 * if an error occurs while connecting
198 private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException {
199 InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr;
201 if (streaming && usingSocks()) {
202 socksConnect(anAddr, aPort, 0);
204 netImpl.connect(fd, normalAddr, aPort, timeout);
206 } catch (ConnectException e) {
207 throw new ConnectException(anAddr + ":" + aPort + " - " + e.getMessage());
209 super.address = normalAddr;
214 protected void create(boolean streaming) throws IOException {
215 this.streaming = streaming;
216 netImpl.socket(fd, streaming);
219 @Override protected void finalize() throws Throwable {
228 protected synchronized InputStream getInputStream() throws IOException {
230 return new SocketInputStream(this);
234 public Object getOption(int optID) throws SocketException {
235 return netImpl.getSocketOption(fd, optID);
239 protected synchronized OutputStream getOutputStream() throws IOException {
241 return new SocketOutputStream(this);
245 protected void listen(int backlog) throws IOException {
247 // Do nothing for a SOCKS connection. The listen occurs on the
248 // server during the bind.
251 netImpl.listen(fd, backlog);
255 public void setOption(int optID, Object val) throws SocketException {
256 netImpl.setSocketOption(fd, optID, val);
260 * Gets the SOCKS proxy server port.
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();
272 * Gets the InetAddress of the SOCKS proxy server.
274 private InetAddress socksGetServerAddress() throws UnknownHostException {
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();
284 return InetAddress.getByName(proxyName);
288 * Connect using a SOCKS server.
290 private void socksConnect(InetAddress applicationServerAddress,
291 int applicationServerPort, int timeout) throws IOException {
293 netImpl.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
294 } catch (Exception e) {
295 throw new SocketException("SOCKS connection failed: " + e);
298 socksRequestConnection(applicationServerAddress, applicationServerPort);
300 lastConnectedAddress = applicationServerAddress;
301 lastConnectedPort = applicationServerPort;
305 * Request a SOCKS connection to the application server given. If the
306 * request fails to complete successfully, an exception is thrown.
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()));
320 * Perform an accept for a SOCKS bind.
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()));
331 * Shutdown the input portion of the socket.
334 protected void shutdownInput() throws IOException {
335 shutdownInput = true;
336 netImpl.shutdownInput(fd);
340 * Shutdown the output portion of the socket.
343 protected void shutdownOutput() throws IOException {
344 netImpl.shutdownOutput(fd);
348 * Bind using a SOCKS server.
350 private void socksBind() throws IOException {
352 netImpl.connect(fd, socksGetServerAddress(), socksGetServerPort(), 0);
353 } catch (Exception e) {
354 throw new IOException("Unable to connect to SOCKS server: " + e);
357 // There must be a connection to an application host for the bind to
359 if (lastConnectedAddress == null) {
360 throw new SocketException("Invalid SOCKS client");
363 // Use the last connected address and port in the bind request.
364 socksSendRequest(Socks4Message.COMMAND_BIND, lastConnectedAddress,
366 Socks4Message reply = socksReadReply();
368 if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
369 throw new IOException(reply.getErrorString(reply
370 .getCommandOrResult()));
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();
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);
385 localport = reply.getPort();
388 private static void intToBytes(int value, byte[] bytes, int start) {
390 * Shift the int so the current byte is right-most Use a byte mask of
391 * 255 to single out the last byte.
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);
400 * Send a SOCKS V4 request.
402 private void socksSendRequest(int command, InetAddress address, int port)
404 Socks4Message request = new Socks4Message();
405 request.setCommandOrResult(command);
406 request.setPort(port);
407 request.setIP(address.getAddress());
408 request.setUserId("default");
410 getOutputStream().write(request.getBytes(), 0, request.getLength());
414 * Read a SOCKS V4 reply.
416 private Socks4Message socksReadReply() throws IOException {
417 Socks4Message reply = new Socks4Message();
419 while (bytesRead < Socks4Message.REPLY_LENGTH) {
420 int count = getInputStream().read(reply.getBytes(), bytesRead,
421 Socks4Message.REPLY_LENGTH - bytesRead);
427 if (Socks4Message.REPLY_LENGTH != bytesRead) {
428 throw new SocketException("Malformed reply from SOCKS server");
434 protected void connect(SocketAddress remoteAddr, int timeout)
436 InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
437 connect(inetAddr.getAddress(), inetAddr.getPort(), timeout);
441 protected boolean supportsUrgentData() {
446 protected void sendUrgentData(int value) throws IOException {
447 netImpl.sendUrgentData(fd, (byte) value);
450 FileDescriptor getFD() {
454 int read(byte[] buffer, int offset, int count) throws IOException {
458 int read = netImpl.read(fd, buffer, offset, count);
459 // Return of zero bytes for a blocking socket means a timeout occurred
461 throw new SocketTimeoutException();
463 // Return of -1 indicates the peer was closed
465 shutdownInput = true;
470 int write(byte[] buffer, int offset, int count) throws IOException {
472 return netImpl.write(fd, buffer, offset, count);
474 return netImpl.send(fd, buffer, offset, count, port, address);