001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.luni.net;
019:
020: import java.io.FileDescriptor;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.InterruptedIOException;
024: import java.io.OutputStream;
025: import java.lang.reflect.Field;
026: import java.net.ConnectException;
027: import java.net.InetAddress;
028: import java.net.InetSocketAddress;
029: import java.net.Proxy;
030: import java.net.SocketAddress;
031: import java.net.SocketException;
032: import java.net.SocketImpl;
033: import java.net.SocketOptions;
034: import java.net.SocketTimeoutException;
035: import java.net.UnknownHostException;
036: import java.security.AccessController;
037: import java.security.PrivilegedAction;
038:
039: import org.apache.harmony.luni.platform.INetworkSystem;
040: import org.apache.harmony.luni.platform.Platform;
041: import org.apache.harmony.luni.util.Msg;
042:
043: /**
044: * A concrete connected-socket implementation.
045: */
046: class PlainSocketImpl extends SocketImpl {
047:
048: // Const copy from socket
049:
050: static final int MULTICAST_IF = 1;
051:
052: static final int MULTICAST_TTL = 2;
053:
054: static final int TCP_NODELAY = 4;
055:
056: static final int FLAG_SHUTDOWN = 8;
057:
058: // For SOCKS support. A SOCKS bind() uses the last
059: // host connected to in its request.
060: static private InetAddress lastConnectedAddress;
061:
062: static private int lastConnectedPort;
063:
064: private static Field fdField;
065:
066: private static Field localportField;
067:
068: private boolean tcpNoDelay = true;
069:
070: /**
071: * used to store the trafficClass value which is simply returned as the
072: * value that was set. We also need it to pass it to methods that specify an
073: * address packets are going to be sent to
074: */
075: private int trafficClass;
076:
077: protected INetworkSystem netImpl = Platform.getNetworkSystem();
078:
079: public int receiveTimeout = 0;
080:
081: public boolean streaming = true;
082:
083: public boolean shutdownInput;
084:
085: Proxy proxy;
086:
087: public PlainSocketImpl() {
088: super ();
089: fd = new FileDescriptor();
090: }
091:
092: @Override
093: protected void accept(SocketImpl newImpl) throws IOException {
094: if (NetUtil.usingSocks(proxy)) {
095: ((PlainSocketImpl) newImpl).socksBind();
096: ((PlainSocketImpl) newImpl).socksAccept();
097: return;
098: }
099:
100: try {
101: if (newImpl instanceof PlainSocketImpl) {
102: PlainSocketImpl newPlainSocketImpl = (PlainSocketImpl) newImpl;
103: netImpl.acceptStreamSocket(fd, newImpl,
104: newPlainSocketImpl.getFileDescriptor(),
105: receiveTimeout);
106: newPlainSocketImpl.setLocalport(getLocalPort());
107: } else {
108: // if newImpl is not an instance of PlainSocketImpl, use
109: // reflection to get/set protected fields.
110: if (null == fdField) {
111: fdField = getSocketImplField("fd"); //$NON-NLS-1$
112: }
113: FileDescriptor newFd = (FileDescriptor) fdField
114: .get(newImpl);
115: netImpl.acceptStreamSocket(fd, newImpl, newFd,
116: receiveTimeout);
117:
118: if (null == localportField) {
119: localportField = getSocketImplField("localport"); //$NON-NLS-1$
120: }
121: localportField.setInt(newImpl, getLocalPort());
122: }
123: } catch (InterruptedIOException e) {
124: throw new SocketTimeoutException(e.getMessage());
125: } catch (IllegalAccessException e) {
126: // empty
127: }
128: }
129:
130: /**
131: * gets SocketImpl field by reflection.
132: */
133: private Field getSocketImplField(final String fieldName) {
134: return AccessController
135: .doPrivileged(new PrivilegedAction<Field>() {
136: public Field run() {
137: Field field = null;
138: try {
139: field = SocketImpl.class
140: .getDeclaredField(fieldName);
141: field.setAccessible(true);
142: } catch (NoSuchFieldException e) {
143: throw new Error(e);
144: }
145: return field;
146: }
147: });
148: }
149:
150: @Override
151: protected synchronized int available() throws IOException {
152: // we need to check if the input has been shutdown. If so
153: // we should return that there is no data to be read
154: if (shutdownInput == true) {
155: return 0;
156: }
157: return netImpl.availableStream(fd);
158: }
159:
160: @Override
161: protected void bind(InetAddress anAddr, int aPort)
162: throws IOException {
163: netImpl.bind(fd, aPort, anAddr);
164: // PlainSocketImpl2.socketBindImpl2(fd, aPort, anAddr);
165: address = anAddr;
166: if (0 != aPort) {
167: localport = aPort;
168: } else {
169: localport = netImpl.getSocketLocalPort(fd, NetUtil
170: .preferIPv6Addresses());
171: }
172: }
173:
174: @Override
175: protected void close() throws IOException {
176: synchronized (fd) {
177: if (fd.valid()) {
178: if ((netImpl.getSocketFlags() & FLAG_SHUTDOWN) != 0) {
179: try {
180: shutdownOutput();
181: } catch (Exception e) {
182: }
183: }
184: netImpl.socketClose(fd);
185: fd = new FileDescriptor();
186: }
187: }
188: }
189:
190: @Override
191: protected void connect(String aHost, int aPort) throws IOException {
192: InetAddress anAddr = netImpl.getHostByName(aHost, NetUtil
193: .preferIPv6Addresses());
194: connect(anAddr, aPort);
195: }
196:
197: @Override
198: protected void connect(InetAddress anAddr, int aPort)
199: throws IOException {
200: connect(anAddr, aPort, 0);
201: }
202:
203: /**
204: * Connects this socket to the specified remote host address/port.
205: *
206: * @param anAddr
207: * the remote host address to connect to
208: * @param aPort
209: * the remote port to connect to
210: * @param timeout
211: * a timeout where supported. 0 means no timeout
212: * @throws IOException
213: * if an error occurs while connecting
214: */
215: private void connect(InetAddress anAddr, int aPort, int timeout)
216: throws IOException {
217: InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress
218: .getLocalHost()
219: : anAddr;
220:
221: try {
222: if (streaming) {
223: if (NetUtil.usingSocks(proxy)) {
224: socksConnect(anAddr, aPort, 0);
225: } else {
226: if (timeout == 0) {
227: netImpl.connect(fd, trafficClass, normalAddr,
228: aPort);
229: } else {
230: netImpl.connectStreamWithTimeoutSocket(fd,
231: aPort, timeout, trafficClass,
232: normalAddr);
233: }
234: }
235: } else {
236: netImpl.connectDatagram(fd, aPort, trafficClass,
237: normalAddr);
238: }
239: } catch (ConnectException e) {
240: throw new ConnectException(anAddr + ":" + aPort + " - "
241: + e.getMessage());
242: }
243: super .address = normalAddr;
244: super .port = aPort;
245: }
246:
247: @Override
248: protected void create(boolean streaming) throws IOException {
249: this .streaming = streaming;
250: if (streaming) {
251: netImpl.createStreamSocket(fd, NetUtil.preferIPv4Stack());
252: } else {
253: netImpl.createDatagramSocket(fd, NetUtil.preferIPv4Stack());
254: }
255: }
256:
257: @Override
258: protected void finalize() throws IOException {
259: close();
260: }
261:
262: @Override
263: protected synchronized InputStream getInputStream()
264: throws IOException {
265: if (!fd.valid()) {
266: throw new SocketException(Msg.getString("K003d"));
267: }
268:
269: return new SocketInputStream(this );
270: }
271:
272: @Override
273: public Object getOption(int optID) throws SocketException {
274: if (optID == SocketOptions.SO_TIMEOUT) {
275: return Integer.valueOf(receiveTimeout);
276: } else if (optID == SocketOptions.IP_TOS) {
277: return Integer.valueOf(trafficClass);
278: } else {
279: // Call the native first so there will be
280: // an exception if the socket if closed.
281: Object result = netImpl.getSocketOption(fd, optID);
282: if (optID == SocketOptions.TCP_NODELAY
283: && (netImpl.getSocketFlags() & TCP_NODELAY) != 0) {
284: return Boolean.valueOf(tcpNoDelay);
285: }
286: return result;
287: }
288: }
289:
290: @Override
291: protected synchronized OutputStream getOutputStream()
292: throws IOException {
293: if (!fd.valid()) {
294: throw new SocketException(Msg.getString("K003d")); //$NON-NLS-1$
295: }
296: return new SocketOutputStream(this );
297: }
298:
299: @Override
300: protected void listen(int backlog) throws IOException {
301: if (NetUtil.usingSocks(proxy)) {
302: // Do nothing for a SOCKS connection. The listen occurs on the
303: // server during the bind.
304: return;
305: }
306: netImpl.listenStreamSocket(fd, backlog);
307: }
308:
309: @Override
310: public void setOption(int optID, Object val) throws SocketException {
311: if (optID == SocketOptions.SO_TIMEOUT) {
312: receiveTimeout = ((Integer) val).intValue();
313: } else {
314: try {
315: netImpl.setSocketOption(fd, optID, val);
316: if (optID == SocketOptions.TCP_NODELAY
317: && (netImpl.getSocketFlags() & TCP_NODELAY) != 0) {
318: tcpNoDelay = ((Boolean) val).booleanValue();
319: }
320: } catch (SocketException e) {
321: // we don't throw an exception for IP_TOS even if the platform
322: // won't let us set the requested value
323: if (optID != SocketOptions.IP_TOS) {
324: throw e;
325: }
326: }
327:
328: /*
329: * save this value as it is actually used differently for IPv4 and
330: * IPv6 so we cannot get the value using the getOption. The option
331: * is actually only set for IPv4 and a masked version of the value
332: * will be set as only a subset of the values are allowed on the
333: * socket. Therefore we need to retain it to return the value that
334: * was set. We also need the value to be passed into a number of
335: * natives so that it can be used properly with IPv6
336: */
337: if (optID == SocketOptions.IP_TOS) {
338: trafficClass = ((Integer) val).intValue();
339: }
340: }
341: }
342:
343: /**
344: * Gets the SOCKS proxy server port.
345: */
346: private int socksGetServerPort() {
347: // get socks server port from proxy. It is unnecessary to check
348: // "socksProxyPort" property, since proxy setting should only be
349: // determined by ProxySelector.
350: InetSocketAddress addr = (InetSocketAddress) proxy.address();
351: return addr.getPort();
352:
353: }
354:
355: /**
356: * Gets the InetAddress of the SOCKS proxy server.
357: */
358: private InetAddress socksGetServerAddress()
359: throws UnknownHostException {
360: String proxyName;
361: // get socks server address from proxy. It is unnecessary to check
362: // "socksProxyHost" property, since all proxy setting should be
363: // determined by ProxySelector.
364: InetSocketAddress addr = (InetSocketAddress) proxy.address();
365: proxyName = addr.getHostName();
366: if (null == proxyName) {
367: proxyName = addr.getAddress().getHostAddress();
368: }
369:
370: InetAddress anAddr = netImpl.getHostByName(proxyName, NetUtil
371: .preferIPv6Addresses());
372: return anAddr;
373: }
374:
375: /**
376: * Connect using a SOCKS server.
377: */
378: private void socksConnect(InetAddress applicationServerAddress,
379: int applicationServerPort, int timeout) throws IOException {
380: try {
381: if (timeout == 0) {
382: netImpl.connect(fd, trafficClass,
383: socksGetServerAddress(), socksGetServerPort());
384: } else {
385: netImpl.connectStreamWithTimeoutSocket(fd,
386: socksGetServerPort(), timeout, trafficClass,
387: socksGetServerAddress());
388: }
389:
390: } catch (Exception e) {
391: throw new SocketException(Msg.getString("K003e", e)); //$NON-NLS-1$
392: }
393:
394: socksRequestConnection(applicationServerAddress,
395: applicationServerPort);
396:
397: lastConnectedAddress = applicationServerAddress;
398: lastConnectedPort = applicationServerPort;
399: }
400:
401: /**
402: * Request a SOCKS connection to the application server given. If the
403: * request fails to complete successfully, an exception is thrown.
404: */
405: private void socksRequestConnection(
406: InetAddress applicationServerAddress,
407: int applicationServerPort) throws IOException {
408: socksSendRequest(Socks4Message.COMMAND_CONNECT,
409: applicationServerAddress, applicationServerPort);
410: Socks4Message reply = socksReadReply();
411: if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
412: throw new IOException(reply.getErrorString(reply
413: .getCommandOrResult()));
414: }
415: }
416:
417: /**
418: * Perform an accept for a SOCKS bind.
419: */
420: public void socksAccept() throws IOException {
421: Socks4Message reply = socksReadReply();
422: if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
423: throw new IOException(reply.getErrorString(reply
424: .getCommandOrResult()));
425: }
426: }
427:
428: /**
429: * Shutdown the input portion of the socket.
430: */
431: @Override
432: protected void shutdownInput() throws IOException {
433: shutdownInput = true;
434: netImpl.shutdownInput(fd);
435: }
436:
437: /**
438: * Shutdown the output portion of the socket.
439: */
440: @Override
441: protected void shutdownOutput() throws IOException {
442: netImpl.shutdownOutput(fd);
443: }
444:
445: /**
446: * Bind using a SOCKS server.
447: */
448: private void socksBind() throws IOException {
449: try {
450: netImpl.connect(fd, trafficClass, socksGetServerAddress(),
451: socksGetServerPort());
452: } catch (Exception e) {
453: throw new IOException(Msg.getString("K003f", e)); //$NON-NLS-1$
454: }
455:
456: // There must be a connection to an application host for the bind to
457: // work.
458: if (lastConnectedAddress == null) {
459: throw new SocketException(Msg.getString("K0040")); //$NON-NLS-1$
460: }
461:
462: // Use the last connected address and port in the bind request.
463: socksSendRequest(Socks4Message.COMMAND_BIND,
464: lastConnectedAddress, lastConnectedPort);
465: Socks4Message reply = socksReadReply();
466:
467: if (reply.getCommandOrResult() != Socks4Message.RETURN_SUCCESS) {
468: throw new IOException(reply.getErrorString(reply
469: .getCommandOrResult()));
470: }
471:
472: // A peculiarity of socks 4 - if the address returned is 0, use the
473: // original socks server address.
474: if (reply.getIP() == 0) {
475: address = socksGetServerAddress();
476: } else {
477: // IPv6 support not yet required as
478: // currently the Socks4Message.getIP() only returns int,
479: // so only works with IPv4 4byte addresses
480: byte[] replyBytes = new byte[4];
481: NetUtil.intToBytes(reply.getIP(), replyBytes, 0);
482: address = InetAddress.getByAddress(replyBytes);
483: }
484: localport = reply.getPort();
485: }
486:
487: /**
488: * Send a SOCKS V4 request.
489: */
490: private void socksSendRequest(int command, InetAddress address,
491: int port) throws IOException {
492: Socks4Message request = new Socks4Message();
493: request.setCommandOrResult(command);
494: request.setPort(port);
495: request.setIP(address.getAddress());
496: request.setUserId("default"); //$NON-NLS-1$
497:
498: getOutputStream().write(request.getBytes(), 0,
499: request.getLength());
500: }
501:
502: /**
503: * Read a SOCKS V4 reply.
504: */
505: private Socks4Message socksReadReply() throws IOException {
506: Socks4Message reply = new Socks4Message();
507: int bytesRead = 0;
508: while (bytesRead < Socks4Message.REPLY_LENGTH) {
509: int count = getInputStream().read(reply.getBytes(),
510: bytesRead, Socks4Message.REPLY_LENGTH - bytesRead);
511: if (-1 == count) {
512: break;
513: }
514: bytesRead += count;
515: }
516: if (Socks4Message.REPLY_LENGTH != bytesRead) {
517: throw new SocketException(Msg.getString("KA011")); //$NON-NLS-1$
518: }
519: return reply;
520: }
521:
522: @Override
523: protected void connect(SocketAddress remoteAddr, int timeout)
524: throws IOException {
525: InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
526: connect(inetAddr.getAddress(), inetAddr.getPort(), timeout);
527: }
528:
529: /**
530: * Answer if the socket supports urgent data.
531: */
532: @Override
533: protected boolean supportsUrgentData() {
534: return !streaming || netImpl.supportsUrgentData(fd);
535: }
536:
537: @Override
538: protected void sendUrgentData(int value) throws IOException {
539: netImpl.sendUrgentData(fd, (byte) value);
540: }
541:
542: FileDescriptor getFD() {
543: return fd;
544: }
545:
546: private void setLocalport(int localport) {
547: this .localport = localport;
548: }
549:
550: int read(byte[] buffer, int offset, int count) throws IOException {
551: if (shutdownInput) {
552: return -1;
553: }
554: try {
555: int read = netImpl.receiveStream(fd, buffer, offset, count,
556: receiveTimeout);
557: if (read == -1) {
558: shutdownInput = true;
559: }
560: return read;
561: } catch (InterruptedIOException e) {
562: throw new SocketTimeoutException(e.getMessage());
563: }
564: }
565:
566: int write(byte[] buffer, int offset, int count) throws IOException {
567: if (!streaming) {
568: return netImpl.sendDatagram2(fd, buffer, offset, count,
569: port, address);
570: }
571: return netImpl.sendStream(fd, buffer, offset, count);
572: }
573: }
|