001: /*
002: * @(#)PlainSocketImpl.java 1.51 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.net;
029:
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.OutputStream;
033: import java.io.InterruptedIOException;
034: import java.io.FileDescriptor;
035: import java.io.ByteArrayOutputStream;
036:
037: import sun.net.ConnectionResetException;
038:
039: /**
040: * Default Socket Implementation. This implementation does
041: * not implement any security checks. It does support SOCKS version 4.
042: * Note this class should <b>NOT</b> be public.
043: *
044: * @author Steven B. Byrne
045: * @version 1.51 10/10/06
046: */
047: class PlainSocketImpl extends SocketImpl {
048: /* instance variable for SO_TIMEOUT */
049: int timeout; // timeout in millisec
050: // traffic class
051: private int trafficClass;
052:
053: private boolean shut_rd = false;
054: private boolean shut_wr = false;
055:
056: private SocketInputStream socketInputStream = null;
057:
058: /* number of threads using the FileDescriptor */
059: private int fdUseCount = 0;
060:
061: /* lock when increment/decrementing fdUseCount */
062: private Object fdLock = new Object();
063:
064: /* indicates a close is pending on the file descriptor */
065: private boolean closePending = false;
066: private static final int REQUEST_REJECTED_NO_IDENTD = 92;
067: private static final int REQUEST_REJECTED_DIFF_IDENTS = 93;
068:
069: /* indicates connection reset state */
070: private int CONNECTION_NOT_RESET = 0;
071: private int CONNECTION_RESET_PENDING = 1;
072: private int CONNECTION_RESET = 2;
073: private int resetState;
074: private Object resetLock = new Object();
075:
076: public static final String socksDefaultPortStr = "1080";
077:
078: /**
079: * Load net library into runtime.
080: */
081: static {
082: java.security.AccessController
083: .doPrivileged(new sun.security.action.LoadLibraryAction(
084: "net"));
085: /*
086: * Wrap initProto() in a privileged block so that we
087: * can read system properties.
088: */
089: java.security.AccessController
090: .doPrivileged(new java.security.PrivilegedAction() {
091: public Object run() {
092: initProto();
093: return null;
094: }
095: });
096: }
097:
098: /**
099: * Constructs an empty instance.
100: */
101: PlainSocketImpl() {
102: }
103:
104: /**
105: * Constructs an instance with the given file descriptor.
106: */
107: PlainSocketImpl(FileDescriptor fd) {
108: this .fd = fd;
109: }
110:
111: /**
112: * Creates a socket with a boolean that specifies whether this
113: * is a stream socket (true) or an unconnected UDP socket (false).
114: */
115: protected synchronized void create(boolean stream)
116: throws IOException {
117: fd = new FileDescriptor();
118: socketCreate(stream);
119: if (socket != null)
120: socket.setCreated();
121: if (serverSocket != null)
122: serverSocket.setCreated();
123: }
124:
125: /**
126: * Creates a socket and connects it to the specified port on
127: * the specified host.
128: * @param host the specified host
129: * @param port the specified port
130: */
131: protected void connect(String host, int port)
132: throws UnknownHostException, IOException {
133: IOException pending = null;
134: try {
135: InetAddress address = InetAddress.getByName(host);
136:
137: try {
138: connectToAddress(address, port, timeout);
139: return;
140: } catch (IOException e) {
141: pending = e;
142: }
143: } catch (UnknownHostException e) {
144: pending = e;
145: }
146:
147: // everything failed
148: close();
149: throw pending;
150: }
151:
152: /**
153: * Creates a socket and connects it to the specified address on
154: * the specified port.
155: * @param address the address
156: * @param port the specified port
157: */
158: protected void connect(InetAddress address, int port)
159: throws IOException {
160: this .port = port;
161: this .address = address;
162:
163: try {
164: connectToAddress(address, port, timeout);
165: return;
166: } catch (IOException e) {
167: // everything failed
168: close();
169: throw e;
170: }
171: }
172:
173: /**
174: * Creates a socket and connects it to the specified address on
175: * the specified port.
176: * @param address the address
177: * @param timeout the timeout value in milliseconds, or zero for no timeout.
178: * @throws IOException if connection fails
179: * @throws IllegalArgumentException if address is null or is a
180: * SocketAddress subclass not supported by this socket
181: * @since 1.4
182: */
183: protected void connect(SocketAddress address, int timeout)
184: throws IOException {
185: if (address == null || !(address instanceof InetSocketAddress))
186: throw new IllegalArgumentException(
187: "unsupported address type");
188: InetSocketAddress addr = (InetSocketAddress) address;
189: if (addr.isUnresolved())
190: throw new UnknownHostException(addr.getHostName());
191: this .port = addr.getPort();
192: this .address = addr.getAddress();
193:
194: try {
195: connectToAddress(this .address, port, timeout);
196: return;
197: } catch (IOException e) {
198: // everything failed
199: close();
200: throw e;
201: }
202: }
203:
204: private void connectToAddress(InetAddress address, int port,
205: int timeout) throws IOException {
206: if (address.isAnyLocalAddress()) {
207: doConnect(InetAddress.getLocalHost(), port, timeout);
208: } else {
209: doConnect(address, port, timeout);
210: }
211: }
212:
213: public void setOption(int opt, Object val) throws SocketException {
214: if (isClosedOrPending()) {
215: throw new SocketException("Socket Closed");
216: }
217: boolean on = true;
218: switch (opt) {
219: /* check type safety b4 going native. These should never
220: * fail, since only java.Socket* has access to
221: * PlainSocketImpl.setOption().
222: */
223: case SO_LINGER:
224: if (val == null
225: || (!(val instanceof Integer) && !(val instanceof Boolean)))
226: throw new SocketException("Bad parameter for option");
227: if (val instanceof Boolean) {
228: /* true only if disabling - enabling should be Integer */
229: on = false;
230: }
231: break;
232: case SO_TIMEOUT:
233: if (val == null || (!(val instanceof Integer)))
234: throw new SocketException(
235: "Bad parameter for SO_TIMEOUT");
236: int tmp = ((Integer) val).intValue();
237: if (tmp < 0)
238: throw new IllegalArgumentException("timeout < 0");
239: timeout = tmp;
240: break;
241: case IP_TOS:
242: if (val == null || !(val instanceof Integer)) {
243: throw new SocketException("bad argument for IP_TOS");
244: }
245: trafficClass = ((Integer) val).intValue();
246: break;
247: case SO_BINDADDR:
248: throw new SocketException("Cannot re-bind socket");
249: case TCP_NODELAY:
250: if (val == null || !(val instanceof Boolean))
251: throw new SocketException(
252: "bad parameter for TCP_NODELAY");
253: on = ((Boolean) val).booleanValue();
254: break;
255: case SO_SNDBUF:
256: case SO_RCVBUF:
257: if (val == null || !(val instanceof Integer)
258: || !(((Integer) val).intValue() > 0)) {
259: throw new SocketException(
260: "bad parameter for SO_SNDBUF " + "or SO_RCVBUF");
261: }
262: break;
263: case SO_KEEPALIVE:
264: if (val == null || !(val instanceof Boolean))
265: throw new SocketException(
266: "bad parameter for SO_KEEPALIVE");
267: on = ((Boolean) val).booleanValue();
268: break;
269: case SO_OOBINLINE:
270: if (val == null || !(val instanceof Boolean))
271: throw new SocketException(
272: "bad parameter for SO_OOBINLINE");
273: on = ((Boolean) val).booleanValue();
274: break;
275: case SO_REUSEADDR:
276: if (val == null || !(val instanceof Boolean))
277: throw new SocketException(
278: "bad parameter for SO_REUSEADDR");
279: on = ((Boolean) val).booleanValue();
280: break;
281: default:
282: throw new SocketException("unrecognized TCP option: " + opt);
283: }
284: socketSetOption(opt, on, val);
285: }
286:
287: public Object getOption(int opt) throws SocketException {
288: if (isClosedOrPending()) {
289: throw new SocketException("Socket Closed");
290: }
291: if (opt == SO_TIMEOUT) {
292: return new Integer(timeout);
293: }
294: int ret = 0;
295: /*
296: * The native socketGetOption() knows about 3 options.
297: * The 32 bit value it returns will be interpreted according
298: * to what we're asking. A return of -1 means it understands
299: * the option but its turned off. It will raise a SocketException
300: * if "opt" isn't one it understands.
301: */
302:
303: switch (opt) {
304: case TCP_NODELAY:
305: ret = socketGetOption(opt, null);
306: return (ret == -1) ? new Boolean(false) : new Boolean(true);
307: case SO_OOBINLINE:
308: ret = socketGetOption(opt, null);
309: return (ret == -1) ? new Boolean(false) : new Boolean(true);
310: case SO_LINGER:
311: ret = socketGetOption(opt, null);
312: return (ret == -1) ? new Boolean(false)
313: : (Object) (new Integer(ret));
314: case SO_REUSEADDR:
315: ret = socketGetOption(opt, null);
316: return (ret == -1) ? new Boolean(false) : new Boolean(true);
317: case SO_BINDADDR:
318: InetAddressContainer in = new InetAddressContainer();
319: ret = socketGetOption(opt, in);
320: return in.addr;
321: case SO_SNDBUF:
322: case SO_RCVBUF:
323: ret = socketGetOption(opt, null);
324: return new Integer(ret);
325: case IP_TOS:
326: ret = socketGetOption(opt, null);
327: if (ret == -1) { // ipv6 tos
328: return new Integer(trafficClass);
329: } else {
330: return new Integer(ret);
331: }
332: case SO_KEEPALIVE:
333: ret = socketGetOption(opt, null);
334: return (ret == -1) ? new Boolean(false) : new Boolean(true);
335: // should never get here
336: default:
337: return null;
338: }
339: }
340:
341: /**
342: * The workhorse of the connection operation. Tries several times to
343: * establish a connection to the given <host, port>. If unsuccessful,
344: * throws an IOException indicating what went wrong.
345: */
346:
347: private synchronized void doConnect(InetAddress address, int port,
348: int timeout) throws IOException {
349: try {
350: FileDescriptor fd = acquireFD();
351: try {
352: socketConnect(address, port, timeout);
353: // If we have a ref. to the Socket, then sets the flags
354: // created, bound & connected to true.
355: // This is normally done in Socket.connect() but some
356: // subclasses of Socket may call impl.connect() directly!
357: if (socket != null) {
358: socket.setBound();
359: socket.setConnected();
360: }
361: } finally {
362: releaseFD();
363: }
364: } catch (IOException e) {
365: close();
366: throw e;
367: }
368: }
369:
370: /**
371: * Binds the socket to the specified address of the specified local port.
372: * @param address the address
373: * @param port the port
374: */
375: protected synchronized void bind(InetAddress address, int lport)
376: throws IOException {
377: socketBind(address, lport);
378: if (socket != null)
379: socket.setBound();
380: if (serverSocket != null)
381: serverSocket.setBound();
382: }
383:
384: /**
385: * Listens, for a specified amount of time, for connections.
386: * @param count the amount of time to listen for connections
387: */
388: protected synchronized void listen(int count) throws IOException {
389: socketListen(count);
390: }
391:
392: /**
393: * Accepts connections.
394: * @param s the connection
395: */
396: protected synchronized void accept(SocketImpl s) throws IOException {
397: FileDescriptor fd = acquireFD();
398: try {
399: socketAccept(s);
400: } finally {
401: releaseFD();
402: }
403: }
404:
405: /**
406: * Gets an InputStream for this socket.
407: */
408: protected synchronized InputStream getInputStream()
409: throws IOException {
410: if (isClosedOrPending()) {
411: throw new IOException("Socket Closed");
412: }
413: if (shut_rd) {
414: throw new IOException("Socket input is shutdown");
415: }
416: if (socketInputStream == null) {
417: socketInputStream = new SocketInputStream(this );
418: }
419: return socketInputStream;
420: }
421:
422: void setInputStream(SocketInputStream in) {
423: socketInputStream = in;
424: }
425:
426: /**
427: * Gets an OutputStream for this socket.
428: */
429: protected synchronized OutputStream getOutputStream()
430: throws IOException {
431: if (isClosedOrPending()) {
432: throw new IOException("Socket Closed");
433: }
434: if (shut_wr) {
435: throw new IOException("Socket output is shutdown");
436: }
437: return new SocketOutputStream(this );
438: }
439:
440: /**
441: * Returns the number of bytes that can be read without blocking.
442: */
443: protected synchronized int available() throws IOException {
444: if (isClosedOrPending()) {
445: throw new IOException("Stream closed.");
446: }
447:
448: /*
449: * If connection has been reset then return 0 to indicate
450: * there are no buffered bytes.
451: */
452: if (isConnectionReset()) {
453: return 0;
454: }
455:
456: /*
457: * If no bytes available and we were previously notified
458: * of a connection reset then we move to the reset state.
459: *
460: * If are notified of a connection reset then check
461: * again if there are bytes buffered on the socket.
462: */
463: int n = 0;
464: try {
465: n = socketAvailable();
466: if (n == 0 && isConnectionResetPending()) {
467: setConnectionReset();
468: }
469: } catch (ConnectionResetException exc1) {
470: setConnectionResetPending();
471: try {
472: n = socketAvailable();
473: if (n == 0) {
474: setConnectionReset();
475: }
476: } catch (ConnectionResetException exc2) {
477: }
478: }
479: return n;
480: }
481:
482: /**
483: * Closes the socket.
484: */
485: protected void close() throws IOException {
486: synchronized (fdLock) {
487: if (fd != null) {
488: if (fdUseCount == 0) {
489: closePending = true;
490: /*
491: * We close the FileDescriptor in two-steps - first the
492: * "pre-close" which closes the socket but doesn't
493: * release the underlying file descriptor. This operation
494: * may be lengthy due to untransmitted data and a long
495: * linger interval. Once the pre-close is done we do the
496: * actual socket to release the fd.
497: */
498: try {
499: socketPreClose();
500: } finally {
501: socketClose();
502: }
503: fd = null;
504: return;
505: } else {
506: /*
507: * If a thread has acquired the fd and a close
508: * isn't pending then use a deferred close.
509: * Also decrement fdUseCount to signal the last
510: * thread that releases the fd to close it.
511: */
512: if (!closePending) {
513: closePending = true;
514: fdUseCount--;
515: socketPreClose();
516: }
517: }
518: }
519: }
520: }
521:
522: /**
523: * Shutdown read-half of the socket connection;
524: */
525: protected void shutdownInput() throws IOException {
526: if (fd != null) {
527: socketShutdown(SHUT_RD);
528: if (socketInputStream != null) {
529: socketInputStream.setEOF(true);
530: }
531: shut_rd = true;
532: }
533: }
534:
535: /**
536: * Shutdown write-half of the socket connection;
537: */
538: protected void shutdownOutput() throws IOException {
539: if (fd != null) {
540: socketShutdown(SHUT_WR);
541: shut_wr = true;
542: }
543: }
544:
545: protected boolean supportsUrgentData() {
546: return true;
547: }
548:
549: protected void sendUrgentData(int data) throws IOException {
550: if (fd == null) {
551: throw new IOException("Socket Closed");
552: }
553: socketSendUrgentData(data);
554: }
555:
556: /**
557: * Cleans up if the user forgets to close it.
558: */
559: protected void finalize() throws IOException {
560: close();
561: }
562:
563: /*
564: * "Acquires" and returns the FileDescriptor for this impl
565: *
566: * A corresponding releaseFD is required to "release" the
567: * FileDescriptor.
568: */
569: public final FileDescriptor acquireFD() {
570: synchronized (fdLock) {
571: fdUseCount++;
572: return fd;
573: }
574: }
575:
576: /*
577: * "Release" the FileDescriptor for this impl.
578: *
579: * If the use count goes to -1 then the socket is closed.
580: */
581: public final void releaseFD() {
582: synchronized (fdLock) {
583: fdUseCount--;
584: if (fdUseCount == -1) {
585: if (fd != null) {
586: try {
587: socketClose();
588: } catch (IOException e) {
589: } finally {
590: fd = null;
591: }
592: }
593: }
594: }
595: }
596:
597: public boolean isConnectionReset() {
598: synchronized (resetLock) {
599: return (resetState == CONNECTION_RESET);
600: }
601: }
602:
603: public boolean isConnectionResetPending() {
604: synchronized (resetLock) {
605: return (resetState == CONNECTION_RESET_PENDING);
606: }
607: }
608:
609: public void setConnectionReset() {
610: synchronized (resetLock) {
611: resetState = CONNECTION_RESET;
612: }
613: }
614:
615: public void setConnectionResetPending() {
616: synchronized (resetLock) {
617: if (resetState == CONNECTION_NOT_RESET) {
618: resetState = CONNECTION_RESET_PENDING;
619: }
620: }
621:
622: }
623:
624: /*
625: * Return true if already closed or close is pending
626: */
627: public boolean isClosedOrPending() {
628: /*
629: * Lock on fdLock to ensure that we wait if a
630: * close is in progress.
631: */
632: synchronized (fdLock) {
633: if (closePending || fd == null) {
634: return true;
635: } else {
636: return false;
637: }
638: }
639: }
640:
641: /*
642: * Return the current value of SO_TIMEOUT
643: */
644: public int getTimeout() {
645: return timeout;
646: }
647:
648: /*
649: * "Pre-close" a socket by dup'ing the file descriptor - this enables
650: * the socket to be closed without releasing the file descriptor.
651: */
652: private void socketPreClose() throws IOException {
653: socketClose0(true);
654: }
655:
656: /*
657: * Close the socket (and release the file descriptor).
658: */
659: private void socketClose() throws IOException {
660: socketClose0(false);
661: }
662:
663: private native void socketCreate(boolean isServer)
664: throws IOException;
665:
666: private native void socketConnect(InetAddress address, int port,
667: int timeout) throws IOException;
668:
669: private native void socketBind(InetAddress address, int port)
670: throws IOException;
671:
672: private native void socketListen(int count) throws IOException;
673:
674: private native void socketAccept(SocketImpl s) throws IOException;
675:
676: private native int socketAvailable() throws IOException;
677:
678: private native void socketClose0(boolean useDeferredClose)
679: throws IOException;
680:
681: private native void socketShutdown(int howto) throws IOException;
682:
683: private static native void initProto();
684:
685: private native void socketSetOption(int cmd, boolean on,
686: Object value) throws SocketException;
687:
688: private native int socketGetOption(int opt, Object iaContainerObj)
689: throws SocketException;
690:
691: private native void socketSendUrgentData(int data)
692: throws IOException;
693:
694: public final static int SHUT_RD = 0;
695: public final static int SHUT_WR = 1;
696: }
697:
698: class InetAddressContainer {
699: InetAddress addr;
700: }
|