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