001: /* jcifs smb client library in Java
002: * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
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:
019: package jcifs.smb;
020:
021: import java.io.InputStream;
022: import java.io.IOException;
023: import jcifs.util.Hexdump;
024:
025: abstract class AndXServerMessageBlock extends ServerMessageBlock {
026:
027: private static final int ANDX_COMMAND_OFFSET = 1;
028: private static final int ANDX_RESERVED_OFFSET = 2;
029: private static final int ANDX_OFFSET_OFFSET = 3;
030:
031: private byte andxCommand = (byte) 0xFF;
032: private int andxOffset = 0;
033:
034: ServerMessageBlock andx = null;
035:
036: AndXServerMessageBlock() {
037: }
038:
039: AndXServerMessageBlock(ServerMessageBlock andx) {
040: this .andx = andx;
041: if (andx != null) {
042: andxCommand = andx.command;
043: }
044: }
045:
046: /* The SmbComReadAndXResponse can read from the InputStream
047: * directly by implementing this method. The default
048: * behavior is to arraycopy all bytes into the buffer and call
049: * readBytesWireFormat. The alternative would have been to overload
050: * the readAndXWireFormat method but that would have resulted in
051: * copying a fairly large chunck of code into the subclass.
052: */
053:
054: abstract int readBytesDirectWireFormat(InputStream in,
055: int byteCount, byte[] buffer, int bufferIndex)
056: throws IOException;
057:
058: int getBatchLimit(byte command) {
059: /* the default limit is 0 batched messages before this
060: * one, meaning this message cannot be batched.
061: */
062: return 0;
063: }
064:
065: /*
066: * We overload this method from ServerMessageBlock because
067: * we want writeAndXWireFormat to write the parameterWords
068: * and bytes. This is so we can write batched smbs because
069: * all but the first smb of the chaain do not have a header
070: * and therefore we do not want to writeHeaderWireFormat. We
071: * just recursivly call writeAndXWireFormat.
072: */
073:
074: int writeWireFormat(byte[] dst, int dstIndex) {
075: int start = headerStart = dstIndex;
076:
077: dstIndex += writeHeaderWireFormat(dst, dstIndex);
078: dstIndex += writeAndXWireFormat(dst, dstIndex);
079: length = dstIndex - start;
080:
081: if (digest != null) {
082: digest.sign(dst, headerStart, length, this , response);
083: }
084:
085: return length;
086: }
087:
088: /*
089: * We overload this because we want readAndXWireFormat to
090: * read the parameter words and bytes. This is so when
091: * commands are batched together we can recursivly call
092: * readAndXWireFormat without reading the non-existent header.
093: */
094:
095: int readWireFormat(InputStream in, byte[] buffer, int bufferIndex)
096: throws IOException {
097: int start = bufferIndex;
098:
099: if (in.read(buffer, bufferIndex, HEADER_LENGTH) != HEADER_LENGTH) {
100: throw new IOException("unexpected EOF reading smb header");
101: }
102: bufferIndex += readHeaderWireFormat(buffer, bufferIndex);
103: bufferIndex += readAndXWireFormat(in, buffer, bufferIndex);
104:
105: length = bufferIndex - start;
106: return length;
107: }
108:
109: int writeAndXWireFormat(byte[] dst, int dstIndex) {
110: int start = dstIndex;
111:
112: wordCount = writeParameterWordsWireFormat(dst, start
113: + ANDX_OFFSET_OFFSET + 2);
114: wordCount += 4; // for command, reserved, and offset
115: dstIndex += wordCount + 1;
116: wordCount /= 2;
117: dst[start] = (byte) (wordCount & 0xFF);
118:
119: byteCount = writeBytesWireFormat(dst, dstIndex + 2);
120: dst[dstIndex++] = (byte) (byteCount & 0xFF);
121: dst[dstIndex++] = (byte) ((byteCount >> 8) & 0xFF);
122: dstIndex += byteCount;
123:
124: /* Normally, without intervention everything would batch
125: * with everything else. If the below clause evaluates true
126: * the andx command will not be written and therefore the
127: * response will not read a batched command and therefore
128: * the 'received' member of the response object will not
129: * be set to true indicating the send and sendTransaction
130: * methods that the next part should be sent. This is a
131: * very indirect and simple batching control mechanism.
132: */
133:
134: if (andx == null || USE_BATCHING == false
135: || batchLevel >= getBatchLimit(andx.command)) {
136: andxCommand = (byte) 0xFF;
137: andx = null;
138:
139: dst[start + ANDX_COMMAND_OFFSET] = (byte) 0xFF;
140: dst[start + ANDX_RESERVED_OFFSET] = (byte) 0x00;
141: dst[start + ANDX_OFFSET_OFFSET] = (byte) 0x00;
142: dst[start + ANDX_OFFSET_OFFSET + 1] = (byte) 0x00;
143:
144: // andx not used; return
145: return dstIndex - start;
146: }
147:
148: /* The message provided to batch has a batchLimit that is
149: * higher than the current batchLevel so we will now encode
150: * that chained message. Before doing so we must increment
151: * the batchLevel of the andx message in case it itself is an
152: * andx message and needs to perform the same check as above.
153: */
154:
155: andx.batchLevel = batchLevel + 1;
156:
157: dst[start + ANDX_COMMAND_OFFSET] = andxCommand;
158: dst[start + ANDX_RESERVED_OFFSET] = (byte) 0x00;
159: andxOffset = dstIndex - headerStart;
160: writeInt2(andxOffset, dst, start + ANDX_OFFSET_OFFSET);
161:
162: andx.useUnicode = useUnicode;
163: if (andx instanceof AndXServerMessageBlock) {
164:
165: /*
166: * A word about communicating header info to andx smbs
167: *
168: * This is where we recursively invoke the provided andx smb
169: * object to write it's parameter words and bytes to our outgoing
170: * array. Incedentally when these andx smbs are created they are not
171: * necessarily populated with header data because they're not writing
172: * the header, only their body. But for whatever reason one might wish
173: * to populate fields if the writeXxx operation needs this header data
174: * for whatever reason. I copy over the uid here so it appears correct
175: * in logging output. Logging of andx segments of messages inadvertantly
176: * print header information because of the way toString always makes a
177: * super.toString() call(see toString() at the end of all smbs classes).
178: */
179:
180: andx.uid = uid;
181: dstIndex += ((AndXServerMessageBlock) andx)
182: .writeAndXWireFormat(dst, dstIndex);
183: } else {
184: // the andx smb is not of type andx so lets just write it here and
185: // were done.
186: int andxStart = dstIndex;
187: andx.wordCount = andx.writeParameterWordsWireFormat(dst,
188: dstIndex);
189: dstIndex += andx.wordCount + 1;
190: andx.wordCount /= 2;
191: dst[andxStart] = (byte) (andx.wordCount & 0xFF);
192:
193: andx.byteCount = andx.writeBytesWireFormat(dst,
194: dstIndex + 2);
195: dst[dstIndex++] = (byte) (andx.byteCount & 0xFF);
196: dst[dstIndex++] = (byte) ((andx.byteCount >> 8) & 0xFF);
197: dstIndex += andx.byteCount;
198: }
199:
200: return dstIndex - start;
201: }
202:
203: int readAndXWireFormat(InputStream in, byte[] buffer,
204: int bufferIndex) throws IOException {
205: int start = bufferIndex;
206:
207: /*
208: * read wordCount
209: */
210:
211: if ((wordCount = in.read()) == -1) {
212: throw new IOException(
213: "unexpected EOF reading smb wordCount");
214: }
215: buffer[bufferIndex++] = (byte) (wordCount & 0xFF);
216:
217: /*
218: * read parameterWords
219: */
220:
221: if (wordCount != 0) {
222: if (in.read(buffer, bufferIndex, wordCount * 2) != (wordCount * 2)) {
223: throw new IOException(
224: "unexpected EOF reading andx parameter words");
225: }
226:
227: /*
228: * these fields are common to all andx commands
229: * so let's populate them here
230: */
231:
232: andxCommand = buffer[bufferIndex];
233: bufferIndex += 2;
234: andxOffset = readInt2(buffer, bufferIndex);
235: bufferIndex += 2;
236:
237: if (andxOffset == 0) { /* Snap server workaround */
238: andxCommand = (byte) 0xFF;
239: }
240:
241: /*
242: * no point in calling readParameterWordsWireFormat if there are no more
243: * parameter words. besides, win98 doesn't return "OptionalSupport" field
244: */
245:
246: if (wordCount > 2) {
247: bufferIndex += readParameterWordsWireFormat(buffer,
248: bufferIndex);
249: }
250: }
251:
252: /*
253: * read byteCount
254: */
255:
256: if (in.read(buffer, bufferIndex, 2) != 2) {
257: throw new IOException(
258: "unexpected EOF reading smb byteCount");
259: }
260: byteCount = readInt2(buffer, bufferIndex);
261: bufferIndex += 2;
262:
263: /*
264: * read bytes
265: */
266:
267: if (byteCount != 0) {
268: int n;
269: n = readBytesDirectWireFormat(in, byteCount, buffer,
270: bufferIndex);
271: if (n == 0) {
272: if (in.read(buffer, bufferIndex, byteCount) != byteCount) {
273: throw new IOException(
274: "unexpected EOF reading andx bytes");
275: }
276: n = readBytesWireFormat(buffer, bufferIndex);
277: }
278: bufferIndex += byteCount;
279: }
280:
281: /*
282: * if there is an andx and it itself is an andx then just recur by
283: * calling this method for it. otherwise just read it's parameter words
284: * and bytes as usual. Note how we can't just call andx.readWireFormat
285: * because there's no header.
286: */
287:
288: if (errorCode != 0 || andxCommand == (byte) 0xFF) {
289: andxCommand = (byte) 0xFF;
290: andx = null;
291: } else if (andx == null) {
292: andxCommand = (byte) 0xFF;
293: throw new IOException(
294: "no andx command supplied with response");
295: } else {
296:
297: /*
298: * This is where we take into account andxOffset
299: *
300: * Before we call readAndXWireFormat on the next andx
301: * part we must take into account the andxOffset. The
302: * input stream must be positioned at this location. The
303: * new location is the just read andxOffset(say 68)
304: * minus the current bufferIndex(say 65). But this packet
305: * construction/deconstruction technique does not require that
306: * the bufferIndex begin at 0. The header might be at another
307: * location(say 4). So we must subtract the current buffer
308: * index from the real start of the header and substract that
309: * from the andxOffset(like 68 - ( 65 - 0 ) if headerStart
310: * were 0 or 68 - ( 69 - 4 ) if the headerStart were 4. We
311: * also need to communicate to our newly instantiated andx
312: * smb the headerStart value so that it may perform the same
313: * calculation as this is a recursive process.
314: */
315:
316: bufferIndex += in.read(buffer, bufferIndex, andxOffset
317: - (bufferIndex - headerStart));
318:
319: andx.headerStart = headerStart;
320: andx.command = andxCommand;
321: andx.errorCode = errorCode;
322: andx.flags = flags;
323: andx.flags2 = flags2;
324: andx.tid = tid;
325: andx.pid = pid;
326: andx.uid = uid;
327: andx.mid = mid;
328: andx.useUnicode = useUnicode;
329:
330: if (andx instanceof AndXServerMessageBlock) {
331: bufferIndex += ((AndXServerMessageBlock) andx)
332: .readAndXWireFormat(in, buffer, andxOffset
333: - headerStart);
334: } else {
335:
336: /*
337: * Just a plain smb. Read it as normal.
338: */
339:
340: /*
341: * read wordCount
342: */
343:
344: if ((andx.wordCount = in.read()) == -1) {
345: throw new IOException(
346: "unexpected EOF reading smb wordCount");
347: }
348: buffer[bufferIndex++] = (byte) (andx.wordCount & 0xFF);
349:
350: /*
351: * read parameterWords
352: */
353:
354: if (andx.wordCount != 0) {
355: if (in
356: .read(buffer, bufferIndex,
357: andx.wordCount * 2) != (andx.wordCount * 2)) {
358: throw new IOException(
359: "unexpected EOF reading andx parameter words");
360: }
361:
362: /*
363: * no point in calling readParameterWordsWireFormat if there are no more
364: * parameter words. besides, win98 doesn't return "OptionalSupport" field
365: */
366:
367: if (andx.wordCount > 2) {
368: bufferIndex += andx
369: .readParameterWordsWireFormat(buffer,
370: bufferIndex);
371: }
372: }
373:
374: /*
375: * read byteCount
376: */
377:
378: if (in.read(buffer, bufferIndex, 2) != 2) {
379: throw new IOException(
380: "unexpected EOF reading smb byteCount");
381: }
382: andx.byteCount = readInt2(buffer, bufferIndex);
383: bufferIndex += 2;
384:
385: /*
386: * read bytes
387: */
388:
389: if (andx.byteCount != 0) {
390: if (in.read(buffer, bufferIndex, andx.byteCount) != andx.byteCount) {
391: throw new IOException(
392: "unexpected EOF reading andx bytes");
393: }
394: andx.readBytesWireFormat(buffer, bufferIndex);
395: bufferIndex += andx.byteCount;
396: }
397: }
398: andx.received = true;
399: }
400:
401: return bufferIndex - start;
402: }
403:
404: public String toString() {
405: return new String(super .toString() + ",andxCommand=0x"
406: + Hexdump.toHexString(andxCommand, 2) + ",andxOffset="
407: + andxOffset);
408: }
409: }
|