blob: 6dd47e917b7978264489f5ce893080339e83b70b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.io.*;
30import java.nio.*;
31
32import javax.net.ssl.SSLException;
33import sun.misc.HexDumpEncoder;
34
35
36/**
37 * A OutputRecord class extension which uses external ByteBuffers
38 * or the internal ByteArrayOutputStream for data manipulations.
39 * <P>
40 * Instead of rewriting this entire class
41 * to use ByteBuffers, we leave things intact, so handshake, CCS,
42 * and alerts will continue to use the internal buffers, but application
43 * data will use external buffers.
44 *
45 * @author Brad Wetmore
46 */
47final class EngineOutputRecord extends OutputRecord {
48
49 private EngineWriter writer;
50
51 private boolean finishedMsg = false;
52
53 /*
54 * All handshake hashing is done by the superclass
55 */
56
57 /*
58 * Default constructor makes a record supporting the maximum
59 * SSL record size. It allocates the header bytes directly.
60 *
61 * @param type the content type for the record
62 */
63 EngineOutputRecord(byte type, SSLEngineImpl engine) {
64 super(type, recordSize(type));
65 writer = engine.writer;
66 }
67
68 /**
69 * Get the size of the buffer we need for records of the specified
70 * type.
71 * <P>
72 * Application data buffers will provide their own byte buffers,
73 * and will not use the internal byte caching.
74 */
75 private static int recordSize(byte type) {
76 switch (type) {
77
78 case ct_change_cipher_spec:
79 case ct_alert:
80 return maxAlertRecordSize;
81
82 case ct_handshake:
83 return maxRecordSize;
84
85 case ct_application_data:
86 return 0;
87 }
88
89 throw new RuntimeException("Unknown record type: " + type);
90 }
91
92 void setFinishedMsg() {
93 finishedMsg = true;
94 }
95
96 public void flush() throws IOException {
97 finishedMsg = false;
98 }
99
100 boolean isFinishedMsg() {
101 return finishedMsg;
102 }
103
104
105 /**
106 * Calculate the MAC value, storing the result either in
107 * the internal buffer, or at the end of the destination
108 * ByteBuffer.
109 * <P>
110 * We assume that the higher levels have assured us enough
111 * room, otherwise we'll indirectly throw a
112 * BufferOverFlowException runtime exception.
113 *
114 * position should equal limit, and points to the next
115 * free spot.
116 */
117 private void addMAC(MAC signer, ByteBuffer bb)
118 throws IOException {
119
120 if (signer.MAClen() != 0) {
121 byte[] hash = signer.compute(contentType(), bb);
122
123 /*
124 * position was advanced to limit in compute above.
125 *
126 * Mark next area as writable (above layers should have
127 * established that we have plenty of room), then write
128 * out the hash.
129 */
130 bb.limit(bb.limit() + hash.length);
131 bb.put(hash);
132 }
133 }
134
135 /*
136 * Encrypt a ByteBuffer.
137 *
138 * We assume that the higher levels have assured us enough
139 * room for the encryption (plus padding), otherwise we'll
140 * indirectly throw a BufferOverFlowException runtime exception.
141 *
142 * position and limit will be the same, and points to the
143 * next free spot.
144 */
145 void encrypt(CipherBox box, ByteBuffer bb) {
146 box.encrypt(bb);
147 }
148
149 /*
150 * Override the actual write below. We do things this way to be
151 * consistent with InputRecord. InputRecord may try to write out
152 * data to the peer, and *then* throw an Exception. This forces
153 * data to be generated/output before the exception is ever
154 * generated.
155 */
156 void writeBuffer(OutputStream s, byte [] buf, int off, int len)
157 throws IOException {
158 /*
159 * Copy data out of buffer, it's ready to go.
160 */
161 ByteBuffer netBB = (ByteBuffer)
162 ByteBuffer.allocate(len).put(buf, 0, len).flip();
163 writer.putOutboundData(netBB);
164 }
165
166 /*
167 * Main method for writing non-application data.
168 * We MAC/encrypt, then send down for processing.
169 */
170 void write(MAC writeMAC, CipherBox writeCipher) throws IOException {
171 /*
172 * Sanity check.
173 */
174 switch (contentType()) {
175 case ct_change_cipher_spec:
176 case ct_alert:
177 case ct_handshake:
178 break;
179 default:
180 throw new RuntimeException("unexpected byte buffers");
181 }
182
183 /*
184 * Don't bother to really write empty records. We went this
185 * far to drive the handshake machinery, for correctness; not
186 * writing empty records improves performance by cutting CPU
187 * time and network resource usage. Also, some protocol
188 * implementations are fragile and don't like to see empty
189 * records, so this increases robustness.
190 *
191 * (Even change cipher spec messages have a byte of data!)
192 */
193 if (!isEmpty()) {
194 // compress(); // eventually
195 addMAC(writeMAC);
196 encrypt(writeCipher);
197 write((OutputStream)null); // send down for processing
198 }
199 return;
200 }
201
202 /**
203 * Main wrap/write driver.
204 */
205 void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher)
206 throws IOException {
207 /*
208 * sanity check to make sure someone didn't inadvertantly
209 * send us an impossible combination we don't know how
210 * to process.
211 */
212 assert(contentType() == ct_application_data);
213
214 /*
215 * Have we set the MAC's yet? If not, we're not ready
216 * to process application data yet.
217 */
218 if (writeMAC == MAC.NULL) {
219 return;
220 }
221
222 /*
223 * Don't bother to really write empty records. We went this
224 * far to drive the handshake machinery, for correctness; not
225 * writing empty records improves performance by cutting CPU
226 * time and network resource usage. Also, some protocol
227 * implementations are fragile and don't like to see empty
228 * records, so this increases robustness.
229 */
230 int length = Math.min(ea.getAppRemaining(), maxDataSize);
231 if (length == 0) {
232 return;
233 }
234
235 /*
236 * Copy out existing buffer values.
237 */
238 ByteBuffer dstBB = ea.netData;
239 int dstPos = dstBB.position();
240 int dstLim = dstBB.limit();
241
242 /*
243 * Where to put the data. Jump over the header.
244 *
245 * Don't need to worry about SSLv2 rewrites, if we're here,
246 * that's long since done.
247 */
248 int dstData = dstPos + headerSize;
249 dstBB.position(dstData);
250
251 ea.gather(length);
252
253 /*
254 * "flip" but skip over header again, add MAC & encrypt
255 * addMAC will expand the limit to reflect the new
256 * data.
257 */
258 dstBB.limit(dstBB.position());
259 dstBB.position(dstData);
260 addMAC(writeMAC, dstBB);
261
262 /*
263 * Encrypt may pad, so again the limit may have changed.
264 */
265 dstBB.limit(dstBB.position());
266 dstBB.position(dstData);
267 encrypt(writeCipher, dstBB);
268
269 if (debug != null
270 && (Debug.isOn("record") || Debug.isOn("handshake"))) {
271 if ((debug != null && Debug.isOn("record"))
272 || contentType() == ct_change_cipher_spec)
273 System.out.println(Thread.currentThread().getName()
274 // v3.0/v3.1 ...
275 + ", WRITE: " + protocolVersion
276 + " " + InputRecord.contentName(contentType())
277 + ", length = " + length);
278 }
279
280 int packetLength = dstBB.limit() - dstData;
281
282 /*
283 * Finish out the record header.
284 */
285 dstBB.put(dstPos, contentType());
286 dstBB.put(dstPos + 1, protocolVersion.major);
287 dstBB.put(dstPos + 2, protocolVersion.minor);
288 dstBB.put(dstPos + 3, (byte)(packetLength >> 8));
289 dstBB.put(dstPos + 4, (byte)packetLength);
290
291 /*
292 * Position was already set by encrypt() above.
293 */
294 dstBB.limit(dstLim);
295
296 return;
297 }
298}