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.FilterOutputStream;
021: import java.io.IOException;
022: import java.io.OutputStream;
023: import java.util.ArrayList;
024: import java.util.List;
025:
026: import net.sourceforge.jtds.jdbc.TdsCore;
027:
028: /**
029: * An output stream that mediates between JSSE and the DB server.
030: * <p/>
031: * SQL Server 2000 has the following requirements:
032: * <ul>
033: * <li>All handshake records are delivered in TDS packets.
034: * <li>The "Client Key Exchange" (CKE), "Change Cipher Spec" (CCS) and
035: * "Finished" (FIN) messages are to be submitted in the delivered in both
036: * the same TDS packet and the same TCP packet.
037: * <li>From then on TLS/SSL records should be transmitted as normal -- the
038: * TDS packet is part of the encrypted application data.
039: *
040: * @author Rob Worsnop
041: * @author Mike Hutchinson
042: * @version $Id: TdsTlsOutputStream.java,v 1.4 2005/04/28 14:29:31 alin_sinpalean Exp $
043: */
044: class TdsTlsOutputStream extends FilterOutputStream {
045: /**
046: * Used for holding back CKE, CCS and FIN records.
047: */
048: final private List bufferedRecords = new ArrayList();
049: private int totalSize;
050:
051: /**
052: * Constructs a TdsTlsOutputStream based on an underlying output stream.
053: *
054: * @param out the underlying output stream
055: */
056: TdsTlsOutputStream(OutputStream out) {
057: super (out);
058: }
059:
060: /**
061: * Holds back a record for batched transmission.
062: *
063: * @param record the TLS record to buffer
064: * @param len the length of the TLS record to buffer
065: */
066: private void deferRecord(byte record[], int len) {
067: byte tmp[] = new byte[len];
068: System.arraycopy(record, 0, tmp, 0, len);
069: bufferedRecords.add(tmp);
070: totalSize += len;
071: }
072:
073: /**
074: * Transmits the buffered batch of records.
075: */
076: private void flushBufferedRecords() throws IOException {
077: byte tmp[] = new byte[totalSize];
078: int off = 0;
079: for (int i = 0; i < bufferedRecords.size(); i++) {
080: byte x[] = (byte[]) bufferedRecords.get(i);
081: System.arraycopy(x, 0, tmp, off, x.length);
082: off += x.length;
083: }
084: putTdsPacket(tmp, off);
085: bufferedRecords.clear();
086: totalSize = 0;
087: }
088:
089: public void write(byte[] b, int off, int len) throws IOException {
090:
091: if (len < Ssl.TLS_HEADER_SIZE || off > 0) {
092: // Too short for a TLS packet just write it
093: out.write(b, off, len);
094: return;
095: }
096: //
097: // Extract relevant TLS header fields
098: //
099: int contentType = b[0] & 0xFF;
100: int length = ((b[3] & 0xFF) << 8) | (b[4] & 0xFF);
101: //
102: // Check to see if probably a SSL client hello
103: //
104: if (contentType < Ssl.TYPE_CHANGECIPHERSPEC
105: || contentType > Ssl.TYPE_APPLICATIONDATA
106: || length != len - Ssl.TLS_HEADER_SIZE) {
107: // Assume SSLV2 Client Hello
108: putTdsPacket(b, len);
109: return;
110: }
111: //
112: // Process TLS records
113: //
114: switch (contentType) {
115:
116: case Ssl.TYPE_APPLICATIONDATA:
117: // Application data, just copy to output
118: out.write(b, off, len);
119: break;
120:
121: case Ssl.TYPE_CHANGECIPHERSPEC:
122: // Cipher spec change has to be buffered
123: deferRecord(b, len);
124: break;
125:
126: case Ssl.TYPE_ALERT:
127: // Alert record ignore!
128: break;
129:
130: case Ssl.TYPE_HANDSHAKE:
131: // TLS Handshake records
132: if (len >= (Ssl.TLS_HEADER_SIZE + Ssl.HS_HEADER_SIZE)) {
133: // Long enough for a handshake subheader
134: int hsType = b[5];
135: int hsLen = (b[6] & 0xFF) << 16 | (b[7] & 0xFF) << 8
136: | (b[8] & 0xFF);
137:
138: if (hsLen == len
139: - (Ssl.TLS_HEADER_SIZE + Ssl.HS_HEADER_SIZE)
140: &&
141: // Client hello has to go in its own TDS packet
142: hsType == Ssl.TYPE_CLIENTHELLO) {
143: putTdsPacket(b, len);
144: break;
145: }
146: // All others have to be deferred and sent as a block
147: deferRecord(b, len);
148: //
149: // Now see if we have a finish record which will flush the
150: // buffered records.
151: //
152: if (hsLen != len
153: - (Ssl.TLS_HEADER_SIZE + Ssl.HS_HEADER_SIZE)
154: || hsType != Ssl.TYPE_CLIENTKEYEXCHANGE) {
155: // This is probably a finish record
156: flushBufferedRecords();
157: }
158: break;
159: }
160: default:
161: // Short or unknown record output it anyway
162: out.write(b, off, len);
163: break;
164: }
165: }
166:
167: /**
168: * Write a TDS packet containing the TLS record(s).
169: *
170: * @param b the TLS record
171: * @param len the length of the TLS record
172: */
173: void putTdsPacket(byte[] b, int len) throws IOException {
174: byte tdsHdr[] = new byte[TdsCore.PKT_HDR_LEN];
175: tdsHdr[0] = TdsCore.PRELOGIN_PKT;
176: tdsHdr[1] = 0x01;
177: tdsHdr[2] = (byte) ((len + TdsCore.PKT_HDR_LEN) >> 8);
178: tdsHdr[3] = (byte) (len + TdsCore.PKT_HDR_LEN);
179: out.write(tdsHdr, 0, tdsHdr.length);
180: out.write(b, 0, len);
181: }
182:
183: /*
184: * (non-Javadoc)
185: *
186: * @see java.io.OutputStream#flush()
187: */
188: public void flush() throws IOException {
189: super.flush();
190: }
191:
192: }
|