001: //jTDS JDBC Driver for Microsoft SQL Server and Sybase
002: //Copyright (C) 2004 The jTDS Project
003: //
004: //This library is free software; you can redistribute it and/or
005: //modify it under the terms of the GNU Lesser General Public
006: //License as published by the Free Software Foundation; either
007: //version 2.1 of the License, or (at your option) any later version.
008: //
009: //This library is distributed in the hope that it will be useful,
010: //but WITHOUT ANY WARRANTY; without even the implied warranty of
011: //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: //Lesser General Public License for more details.
013: //
014: //You should have received a copy of the GNU Lesser General Public
015: //License along with this library; if not, write to the Free Software
016: //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: //
018: package net.sourceforge.jtds.ssl;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.FilterInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024:
025: import net.sourceforge.jtds.jdbc.TdsCore;
026:
027: /**
028: * An input stream that filters out TDS headers so they are not returned to
029: * JSSE (which will not recognize them).
030: *
031: * @author Rob Worsnop
032: * @author Mike Hutchinson
033: * @version $Id: TdsTlsInputStream.java,v 1.5 2005/10/20 09:46:38 alin_sinpalean Exp $
034: */
035: class TdsTlsInputStream extends FilterInputStream {
036:
037: int bytesOutstanding;
038:
039: /**
040: * Temporary buffer used to de-encapsulate inital TLS packets.
041: * Initial size should be enough for login phase after which no
042: * buffering is required.
043: */
044: final byte[] readBuffer = new byte[6144];
045:
046: InputStream bufferStream;
047:
048: /** False if TLS packets are encapsulated in TDS packets. */
049: boolean pureSSL;
050:
051: /**
052: * Constructs a TdsTlsInputStream and bases it on an underlying stream.
053: *
054: * @param in the underlying stream
055: */
056: public TdsTlsInputStream(InputStream in) {
057: super (in);
058: }
059:
060: /*
061: * (non-Javadoc)
062: *
063: * @see java.io.InputStream#read(byte[], int, int)
064: */
065: public int read(byte[] b, int off, int len) throws IOException {
066:
067: //
068: // If we have read past the TDS encapsulated TLS records
069: // Just read directly from the input stream.
070: //
071: if (pureSSL && bufferStream == null) {
072: return in.read(b, off, len);
073: }
074:
075: // If this is the start of a new TLS record or
076: // TDS packet we need to read in entire record/packet.
077: if (!pureSSL && bufferStream == null) {
078: primeBuffer();
079: }
080:
081: // Feed the client code bytes from the buffer
082: int ret = bufferStream.read(b, off, len);
083: bytesOutstanding -= ret < 0 ? 0 : ret;
084: if (bytesOutstanding == 0) {
085: // All bytes in the buffer have been read.
086: // The next read will prime it again.
087: bufferStream = null;
088: }
089:
090: return ret;
091: }
092:
093: /**
094: * Read in entire TLS record or TDS packet and store the TLS record in the
095: * buffer. (TDS packets will always contain a TLS record.)
096: */
097: private void primeBuffer() throws IOException {
098: // first read the type (first byte for TDS and TLS).
099: // TLS packet hdr size = 5 TDS = 8
100: readFully(readBuffer, 0, Ssl.TLS_HEADER_SIZE);
101: int len;
102: if (readBuffer[0] == TdsCore.REPLY_PKT
103: || readBuffer[0] == TdsCore.PRELOGIN_PKT) {
104: len = ((readBuffer[2] & 0xFF) << 8)
105: | (readBuffer[3] & 0xFF);
106: // Read rest of header to skip
107: readFully(readBuffer, Ssl.TLS_HEADER_SIZE,
108: TdsCore.PKT_HDR_LEN - Ssl.TLS_HEADER_SIZE);
109: len -= TdsCore.PKT_HDR_LEN;
110: readFully(readBuffer, 0, len); // Now get inner packet
111: } else {
112: len = ((readBuffer[3] & 0xFF) << 8)
113: | (readBuffer[4] & 0xFF);
114: readFully(readBuffer, Ssl.TLS_HEADER_SIZE, len
115: - Ssl.TLS_HEADER_SIZE);
116: pureSSL = true;
117: }
118:
119: bufferStream = new ByteArrayInputStream(readBuffer, 0, len);
120: bytesOutstanding = len;
121: }
122:
123: /**
124: * Reads <code>len</code> bytes or throws an <code>IOException</code> if
125: * there aren't that many bytes available.
126: *
127: * @param b buffer to read into
128: * @param off offset in the buffer where to start storing
129: * @param len amount of data to read
130: * @throws IOException if an I/O error occurs or not enough data is
131: * available
132: */
133: private void readFully(byte[] b, int off, int len)
134: throws IOException {
135: int res = 0;
136: while (len > 0 && (res = in.read(b, off, len)) >= 0) {
137: off += res;
138: len -= res;
139: }
140:
141: if (res < 0) {
142: throw new IOException();
143: }
144: }
145: }
|