1 /*
2  * Copyright (c) 2001, 2003, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.jndi.ldap.sasl;
27 
28 import javax.security.sasl.Sasl;
29 import javax.security.sasl.SaslClient;
30 import javax.security.sasl.SaslException;
31 import java.io.IOException;
32 import java.io.FilterOutputStream;
33 import java.io.OutputStream;
34 
35 class SaslOutputStream extends FilterOutputStream {
36     private static final boolean debug = false;
37 
38     private byte[] lenBuf = new byte[4];  // buffer for storing length
39     private int rawSendSize = 65536;
40     private SaslClient sc;
41 
SaslOutputStream(SaslClient sc, OutputStream out)42     SaslOutputStream(SaslClient sc, OutputStream out) throws SaslException {
43         super(out);
44         this.sc = sc;
45 
46         if (debug) {
47             System.err.println("SaslOutputStream: " + out);
48         }
49 
50         String str = (String) sc.getNegotiatedProperty(Sasl.RAW_SEND_SIZE);
51         if (str != null) {
52             try {
53                 rawSendSize = Integer.parseInt(str);
54             } catch (NumberFormatException e) {
55                 throw new SaslException(Sasl.RAW_SEND_SIZE +
56                     " property must be numeric string: " + str);
57             }
58         }
59     }
60 
61     // Override this method to call write(byte[], int, int) counterpart
62     // super.write(int) simply calls out.write(int)
63 
write(int b)64     public void write(int b) throws IOException {
65         byte[] buffer = new byte[1];
66         buffer[0] = (byte)b;
67         write(buffer, 0, 1);
68     }
69 
70     /**
71      * Override this method to "wrap" the outgoing buffer before
72      * writing it to the underlying output stream.
73      */
write(byte[] buffer, int offset, int total)74     public void write(byte[] buffer, int offset, int total) throws IOException {
75         int count;
76         byte[] wrappedToken, saslBuffer;
77 
78         // "Packetize" buffer to be within rawSendSize
79         if (debug) {
80             System.err.println("Total size: " + total);
81         }
82 
83         for (int i = 0; i < total; i += rawSendSize) {
84 
85             // Calculate length of current "packet"
86             count = (total - i) < rawSendSize ? (total - i) : rawSendSize;
87 
88             // Generate wrapped token
89             wrappedToken = sc.wrap(buffer, offset+i, count);
90 
91             // Write out length
92             intToNetworkByteOrder(wrappedToken.length, lenBuf, 0, 4);
93 
94             if (debug) {
95                 System.err.println("sending size: " + wrappedToken.length);
96             }
97             out.write(lenBuf, 0, 4);
98 
99             // Write out wrapped token
100             out.write(wrappedToken, 0, wrappedToken.length);
101         }
102     }
103 
104     public void close() throws IOException {
105         SaslException save = null;
106         try {
107             sc.dispose();  // Dispose of SaslClient's state
108         } catch (SaslException e) {
109             // Save exception for throwing after closing 'in'
110             save = e;
111         }
112         super.close();  // Close underlying output stream
113 
114         if (save != null) {
115             throw save;
116         }
117     }
118 
119     // Copied from com.sun.security.sasl.util.SaslImpl
120     /**
121      * Encodes an integer into 4 bytes in network byte order in the buffer
122      * supplied.
123      */
124     private static void intToNetworkByteOrder(int num, byte[] buf, int start,
125         int count) {
126         if (count > 4) {
127             throw new IllegalArgumentException("Cannot handle more than 4 bytes");
128         }
129 
130         for (int i = count-1; i >= 0; i--) {
131             buf[start+i] = (byte)(num & 0xff);
132             num >>>= 8;
133         }
134     }
135 }
136