001: /* jcifs smb client library in Java
002: * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
003: * "Eric Glass" <jcifs at samba dot org>
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package com.knowgate.jcifs.smb;
021:
022: import com.knowgate.jcifs.netbios.NbtSocket;
023: import com.knowgate.jcifs.netbios.NbtException;
024: import com.knowgate.jcifs.netbios.NbtAddress;
025: import com.knowgate.jcifs.UniAddress;
026: import com.knowgate.jcifs.Config;
027: import com.knowgate.debug.*;
028:
029: import java.io.InputStream;
030: import java.io.OutputStream;
031: import java.io.PushbackInputStream;
032: import java.io.IOException;
033: import java.io.InterruptedIOException;
034: import java.net.InetAddress;
035: import java.net.SocketException;
036: import java.net.UnknownHostException;
037:
038: import java.security.MessageDigest;
039:
040: import java.util.Arrays;
041: import java.util.LinkedList;
042: import java.util.ListIterator;
043: import java.util.Enumeration;
044: import java.util.HashMap;
045:
046: class SmbTransport implements Runnable {
047:
048: private static final int DEFAULT_MAX_MPX_COUNT = 10;
049: private static final int DEFAULT_RESPONSE_TIMEOUT = 10000;
050: private static final int DEFAULT_SO_TIMEOUT = 15000;
051: private static final int DEFAULT_RCV_BUF_SIZE = 60416;
052: private static final int DEFAULT_SND_BUF_SIZE = 5000;
053: private static final int DEFAULT_SSN_LIMIT = 250;
054:
055: private static final InetAddress LADDR = Config.getInetAddress(
056: "jcifs.smb.client.laddr", null);
057: private static final int LPORT = Config.getInt(
058: "jcifs.smb.client.lport", 0);
059: private static final int SSN_LIMIT = Config.getInt(
060: "jcifs.smb.client.ssnLimit", DEFAULT_SSN_LIMIT);
061: private static final int MAX_MPX_COUNT = Config.getInt(
062: "jcifs.smb.client.maxMpxCount", DEFAULT_MAX_MPX_COUNT);
063: private static final int SND_BUF_SIZE = Config.getInt(
064: "jcifs.smb.client.snd_buf_size", DEFAULT_SND_BUF_SIZE);
065: private static final int RCV_BUF_SIZE = Config.getInt(
066: "jcifs.smb.client.rcv_buf_size", DEFAULT_RCV_BUF_SIZE);
067: private static final boolean USE_UNICODE = Config.getBoolean(
068: "jcifs.smb.client.useUnicode", true);
069: private static final boolean FORCE_UNICODE = Config.getBoolean(
070: "jcifs.smb.client.useUnicode", false);
071: private static final boolean USE_NTSTATUS = Config.getBoolean(
072: "jcifs.smb.client.useNtStatus", true);
073: private static final boolean SIGNPREF = Config.getBoolean(
074: "jcifs.smb.client.signingPreferred", false);
075: private static final boolean USE_NTSMBS = Config.getBoolean(
076: "jcifs.smb.client.useNTSmbs", true);
077: private static final int DEFAULT_FLAGS2 = ServerMessageBlock.FLAGS2_LONG_FILENAMES
078: | ServerMessageBlock.FLAGS2_EXTENDED_ATTRIBUTES
079: | (SIGNPREF ? ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES
080: : 0)
081: | (USE_NTSTATUS ? ServerMessageBlock.FLAGS2_STATUS32 : 0)
082: | (USE_UNICODE ? ServerMessageBlock.FLAGS2_UNICODE : 0);
083: private static final int DEFAULT_CAPABILITIES = (USE_NTSMBS ? ServerMessageBlock.CAP_NT_SMBS
084: : 0)
085: | (USE_NTSTATUS ? ServerMessageBlock.CAP_STATUS32 : 0)
086: | (USE_UNICODE ? ServerMessageBlock.CAP_UNICODE : 0)
087: | ServerMessageBlock.CAP_DFS;
088: private static final int FLAGS2 = Config.getInt(
089: "jcifs.smb.client.flags2", DEFAULT_FLAGS2);
090: private static final int CAPABILITIES = Config.getInt(
091: "jcifs.smb.client.capabilities", DEFAULT_CAPABILITIES);
092: private static final int SO_TIMEOUT = Config.getInt(
093: "jcifs.smb.client.soTimeout", DEFAULT_SO_TIMEOUT);
094: private static final boolean TCP_NODELAY = Config.getBoolean(
095: "jcifs.smb.client.tcpNoDelay", false);
096: private static final int RESPONSE_TIMEOUT = Config.getInt(
097: "jcifs.smb.client.responseTimeout",
098: DEFAULT_RESPONSE_TIMEOUT);
099:
100: private static final int PUSHBACK_BUF_SIZE = 64;
101: private static final int MID_OFFSET = 30;
102: private static final int FLAGS_RESPONSE = 0x80;
103: private static final int ST_GROUND = 0;
104: private static final int ST_NEGOTIATING = 1;
105: private static final LinkedList CONNECTIONS = new LinkedList();
106: private static final int MAGIC[] = { 0xFF, 'S', 'M', 'B' };
107:
108: private static byte[] snd_buf = new byte[0xFFFF];
109: private static byte[] rcv_buf = new byte[0xFFFF];
110:
111: private NbtSocket socket;
112: private HashMap responseTable;
113: private InputStream in;
114: private OutputStream out;
115: private int localPort;
116: private InetAddress localAddr;
117: private Thread thread;
118: private Object outLock;
119: private UniAddress address;
120: private int port;
121: private LinkedList sessions;
122: private LinkedList referrals = new LinkedList();
123: private int state;
124: private Mid[] mids = new Mid[MAX_MPX_COUNT];
125: private short mid_next;
126:
127: static final String NATIVE_OS = Config.getProperty(
128: "jcifs.smb.client.nativeOs", System.getProperty("os.name"));
129: static final String NATIVE_LANMAN = Config.getProperty(
130: "jcifs.smb.client.nativeLanMan", "jCIFS");
131: static final int VC_NUMBER = 1;
132:
133: static final SmbTransport NULL_TRANSPORT = new SmbTransport();
134:
135: class Mid {
136: short mid;
137:
138: public int hashCode() {
139: return mid;
140: }
141:
142: public boolean equals(Object obj) {
143: return ((Mid) obj).mid == mid;
144: }
145: }
146:
147: class ServerData {
148: byte flags;
149: int flags2;
150: int maxMpxCount;
151: int maxBufferSize;
152: int sessionKey;
153: // NT 4 Workstation is 0x43FD
154: int capabilities;
155: String oemDomainName;
156: int securityMode;
157: int security;
158: boolean encryptedPasswords;
159: boolean signaturesEnabled;
160: boolean signaturesRequired;
161: int maxNumberVcs;
162: int maxRawSize;
163: long serverTime;
164: int serverTimeZone;
165: int encryptionKeyLength;
166: byte[] encryptionKey;
167: }
168:
169: int flags2 = FLAGS2;
170: int maxMpxCount = MAX_MPX_COUNT;
171: int snd_buf_size = SND_BUF_SIZE;
172: int rcv_buf_size = RCV_BUF_SIZE;
173: int capabilities = CAPABILITIES;
174: int sessionKey = 0x00000000;
175: boolean useUnicode = USE_UNICODE;
176: String tconHostName;
177: ServerData server;
178: SigningDigest digest = null;
179:
180: static synchronized SmbTransport getSmbTransport(
181: UniAddress address, int port) {
182: return getSmbTransport(address, port, LADDR, LPORT);
183: }
184:
185: static synchronized SmbTransport getSmbTransport(
186: UniAddress address, int port, InetAddress localAddr,
187: int localPort) {
188: SmbTransport conn;
189:
190: synchronized (CONNECTIONS) {
191: if (SSN_LIMIT != 1) {
192: ListIterator iter = CONNECTIONS.listIterator();
193: while (iter.hasNext()) {
194: conn = (SmbTransport) iter.next();
195: if (conn.matches(address, port, localAddr,
196: localPort)
197: && (SSN_LIMIT == 0 || conn.sessions.size() < SSN_LIMIT)) {
198: return conn;
199: }
200: }
201: }
202:
203: conn = new SmbTransport(address, port, localAddr, localPort);
204: CONNECTIONS.add(0, conn);
205: }
206:
207: return conn;
208: }
209:
210: SmbTransport(UniAddress address, int port, InetAddress localAddr,
211: int localPort) {
212: this .address = address;
213: this .port = port;
214: this .localAddr = localAddr;
215: this .localPort = localPort;
216:
217: sessions = new LinkedList();
218: responseTable = new HashMap();
219: outLock = new Object();
220: state = ST_GROUND;
221:
222: int i;
223: for (i = 0; i < MAX_MPX_COUNT; i++) {
224: mids[i] = new Mid();
225: }
226: }
227:
228: SmbTransport() {
229: }
230:
231: synchronized SmbSession getSmbSession() {
232: return getSmbSession(new NtlmPasswordAuthentication(null, null,
233: null));
234: }
235:
236: synchronized SmbSession getSmbSession(
237: NtlmPasswordAuthentication auth) {
238: SmbSession ssn;
239:
240: ListIterator iter = sessions.listIterator();
241: while (iter.hasNext()) {
242: ssn = (SmbSession) iter.next();
243: if (ssn.matches(auth)) {
244: ssn.auth = auth;
245: return ssn;
246: }
247: }
248: ssn = new SmbSession(address, port, localAddr, localPort, auth);
249: ssn.transport = this ;
250: sessions.add(ssn);
251:
252: return ssn;
253: }
254:
255: boolean matches(UniAddress address, int port,
256: InetAddress localAddr, int localPort) {
257: InetAddress defaultLocal = null;
258: try {
259: defaultLocal = InetAddress.getLocalHost();
260: } catch (UnknownHostException uhe) {
261: }
262: int p1 = (port == 0 || port == 139) ? 0 : port;
263: int p2 = (this .port == 0 || this .port == 139) ? 0 : this .port;
264: InetAddress la1 = localAddr == null ? defaultLocal : localAddr;
265: InetAddress la2 = this .localAddr == null ? defaultLocal
266: : this .localAddr;
267: return address.equals(this .address) && p1 == p2
268: && la1.equals(la2) && localPort == this .localPort;
269: }
270:
271: boolean hasCapability(int cap) throws SmbException {
272: if (state == ST_GROUND) {
273: negotiate();
274: }
275: return (capabilities & cap) == cap;
276: }
277:
278: boolean isSignatureSetupRequired(NtlmPasswordAuthentication auth) {
279: return (flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES) != 0
280: && digest == null
281: && auth != NtlmPasswordAuthentication.NULL
282: && NtlmPasswordAuthentication.NULL.equals(auth) == false;
283: }
284:
285: void ensureOpen() throws IOException {
286: if (socket == null) {
287: Object obj;
288: NbtAddress naddr;
289: String calledName;
290:
291: obj = address.getAddress();
292: if (obj instanceof NbtAddress) {
293: naddr = (NbtAddress) obj;
294: } else {
295: try {
296: naddr = NbtAddress.getByName(((InetAddress) obj)
297: .getHostAddress());
298: } catch (UnknownHostException uhe) {
299: naddr = null; // never happen
300: }
301: }
302:
303: calledName = address.firstCalledName();
304: do {
305: try {
306: socket = new NbtSocket(naddr, calledName, port,
307: localAddr, localPort);
308: break;
309: } catch (NbtException ne) {
310: if (ne.errorClass == NbtException.ERR_SSN_SRVC
311: && (ne.errorCode == NbtException.CALLED_NOT_PRESENT || ne.errorCode == NbtException.NOT_LISTENING_CALLED)) {
312: if (DebugFile.trace)
313: new ErrorHandler(ne);
314: } else {
315: throw ne;
316: }
317: }
318: } while ((calledName = address.nextCalledName()) != null);
319:
320: if (calledName == null) {
321: throw new IOException(
322: "Failed to establish session with " + address);
323: }
324:
325: /* Save the calledName for using on SMB_COM_TREE_CONNECT
326: */
327: if (calledName == NbtAddress.SMBSERVER_NAME) {
328: tconHostName = address.getHostAddress();
329: } else {
330: tconHostName = calledName;
331: }
332:
333: if (TCP_NODELAY) {
334: socket.setTcpNoDelay(true);
335: }
336: in = new PushbackInputStream(socket.getInputStream(),
337: PUSHBACK_BUF_SIZE);
338: out = socket.getOutputStream();
339: thread = new Thread(this , "JCIFS-SmbTransport");
340: thread.setDaemon(true);
341: thread.start();
342: }
343: }
344:
345: void tryClose(boolean inError) {
346: SmbSession ssn;
347:
348: if (socket == null) {
349: inError = true;
350: }
351:
352: ListIterator iter = sessions.listIterator();
353: while (iter.hasNext()) {
354: ssn = (SmbSession) iter.next();
355: ssn.logoff(inError);
356: }
357: if (socket != null) {
358: try {
359: socket.close();
360: } catch (IOException ioe) {
361: }
362: }
363: digest = null;
364: in = null;
365: out = null;
366: socket = null;
367: thread = null;
368: responseTable.clear();
369: referrals.clear();
370: sessions.clear();
371: synchronized (CONNECTIONS) {
372: CONNECTIONS.remove(this );
373: }
374: state = ST_GROUND;
375: }
376:
377: public void run() {
378: Mid mid = new Mid();
379: int i, m, nbtlen;
380: ServerMessageBlock response;
381:
382: while (thread == Thread.currentThread()) {
383: try {
384: socket.setSoTimeout(SO_TIMEOUT);
385:
386: m = 0;
387: while (m < 4) {
388: if ((i = in.read()) < 0) {
389: return;
390: }
391: if ((i & 0xFF) == MAGIC[m]) {
392: m++;
393: } else if ((i & 0xFF) == 0xFF) {
394: m = 1;
395: } else {
396: m = 0;
397: }
398: }
399:
400: nbtlen = 4 + in.available();
401:
402: synchronized (rcv_buf) {
403: rcv_buf[0] = (byte) 0xFF;
404: rcv_buf[1] = (byte) 'S';
405: rcv_buf[2] = (byte) 'M';
406: rcv_buf[3] = (byte) 'B';
407:
408: if (in.read(rcv_buf, 4,
409: ServerMessageBlock.HEADER_LENGTH - 4) != (ServerMessageBlock.HEADER_LENGTH - 4)) {
410: /* Read on a netbios SocketInputStream does not
411: * return until len bytes have been read or the stream is
412: * closed. This must be the latter case.
413: */
414: break;
415: }
416: ((PushbackInputStream) in).unread(rcv_buf, 0,
417: ServerMessageBlock.HEADER_LENGTH);
418: if (rcv_buf[0] != (byte) 0xFF
419: || rcv_buf[1] != (byte) 'S'
420: || rcv_buf[2] != (byte) 'M'
421: || rcv_buf[3] != (byte) 'B') {
422: if (DebugFile.trace)
423: DebugFile
424: .writeln("bad smb header, purging session message: "
425: + address);
426: in.skip(in.available());
427: continue;
428: }
429: if ((rcv_buf[ServerMessageBlock.FLAGS_OFFSET] & ServerMessageBlock.FLAGS_RESPONSE) == ServerMessageBlock.FLAGS_RESPONSE) {
430: mid.mid = (short) (ServerMessageBlock.readInt2(
431: rcv_buf, MID_OFFSET) & 0xFFFF);
432:
433: response = (ServerMessageBlock) responseTable
434: .get(mid);
435: if (response == null) {
436: if (DebugFile.trace) {
437: DebugFile.writeln("no handler for mid="
438: + mid.mid
439: + ", purging session message: "
440: + address);
441: }
442: in.skip(in.available());
443: continue;
444: }
445: synchronized (response) {
446: response.useUnicode = useUnicode;
447:
448: if (DebugFile.trace)
449: DebugFile
450: .writeln("new data read from socket: "
451: + address);
452:
453: if (response instanceof SmbComTransactionResponse) {
454: Enumeration e = (Enumeration) response;
455: if (e.hasMoreElements()) {
456: e.nextElement();
457: } else {
458: if (DebugFile.trace)
459: DebugFile
460: .writeln("more responses to transaction than expected");
461: continue;
462: }
463: if ((m = response.readWireFormat(in,
464: rcv_buf, 0)) != nbtlen) {
465: if (DebugFile.trace) {
466: DebugFile
467: .writeln("decoded "
468: + m
469: + " but nbtlen="
470: + nbtlen
471: + ", purging session message");
472: }
473: in.skip(in.available());
474: }
475: response.received = true;
476:
477: if (response.errorCode != 0
478: || e.hasMoreElements() == false) {
479: ((SmbComTransactionResponse) response).hasMore = false;
480: if (digest != null) {
481: synchronized (outLock) {
482: digest.verify(rcv_buf, 0,
483: response);
484: }
485: }
486: response.notify();
487: } else {
488: ensureOpen();
489: }
490: } else {
491: response.readWireFormat(in, rcv_buf, 0);
492: response.received = true;
493:
494: if (digest != null) {
495: synchronized (outLock) {
496: digest.verify(rcv_buf, 0,
497: response);
498: }
499: }
500:
501: response.notify();
502: }
503: }
504: } else {
505: // it's a request(break oplock)
506: }
507: }
508: } catch (InterruptedIOException iioe) {
509: if (responseTable.size() == 0) {
510: tryClose(false);
511: } else if (DebugFile.trace) {
512: DebugFile
513: .writeln("soTimeout has occured but there are "
514: + responseTable.size()
515: + " pending requests");
516: }
517: } catch (IOException ioe) {
518: synchronized (this ) {
519: tryClose(true);
520: }
521: if (DebugFile.trace
522: && ioe.getMessage().startsWith(
523: "Connection reset") == false) {
524: DebugFile
525: .writeln(ioe.getMessage() + ": " + address);
526: new ErrorHandler(ioe);
527: }
528: }
529: }
530: }
531:
532: synchronized DfsReferral getDfsReferral(
533: NtlmPasswordAuthentication auth, String path)
534: throws SmbException {
535: String subpath, node, host;
536: DfsReferral dr = new DfsReferral();
537: int p, n, i, s;
538: UniAddress addr;
539:
540: SmbTree ipc = getSmbSession(auth).getSmbTree("IPC$", null);
541: Trans2GetDfsReferralResponse resp = new Trans2GetDfsReferralResponse();
542: ipc.sendTransaction(new Trans2GetDfsReferral(path), resp);
543:
544: subpath = path.substring(0, resp.pathConsumed);
545: node = resp.referral.node;
546: if (subpath.charAt(0) != '\\'
547: || (i = subpath.indexOf('\\', 1)) < 2
548: || (p = subpath.indexOf('\\', i + 1)) < (i + 2)
549: || node.charAt(0) != '\\'
550: || (s = node.indexOf('\\', 1)) < 2) {
551: throw new SmbException("Invalid DFS path: " + path);
552: }
553: if ((n = node.indexOf('\\', s + 1)) == -1) {
554: n = node.length();
555: }
556:
557: dr.path = subpath.substring(p);
558: dr.node = node.substring(0, n);
559: dr.nodepath = node.substring(n);
560: dr.server = node.substring(1, s);
561: dr.share = node.substring(s + 1, n);
562: dr.resolveHashes = auth.hashesExternal;
563: /* NTLM HTTP Authentication must be re-negotiated
564: * with challenge from 'server' to access DFS vol. */
565: return dr;
566: }
567:
568: synchronized DfsReferral lookupReferral(String unc) {
569: DfsReferral dr;
570: ListIterator iter = referrals.listIterator();
571: int i, len;
572:
573: while (iter.hasNext()) {
574: dr = (DfsReferral) iter.next();
575: len = dr.path.length();
576: for (i = 0; i < len && i < unc.length(); i++) {
577: if (dr.path.charAt(i) != unc.charAt(i)) {
578: break;
579: }
580: }
581: if (i == len
582: && (len == unc.length() || unc.charAt(len) == '\\')) {
583: return dr;
584: }
585: }
586:
587: return null;
588: }
589:
590: void send(ServerMessageBlock request, ServerMessageBlock response)
591: throws SmbException {
592: Mid mid = null;
593:
594: if (state == ST_GROUND) {
595: negotiate();
596: }
597:
598: request.flags2 |= flags2;
599: request.useUnicode = useUnicode;
600:
601: if (response == null) {
602: try {
603: synchronized (outLock) {
604: mid = aquireMid();
605: request.mid = mid.mid;
606: ensureOpen();
607: synchronized (snd_buf) {
608: request.digest = digest;
609: request.response = null;
610: int length = request
611: .writeWireFormat(snd_buf, 4);
612: out.write(snd_buf, 4, length);
613: out.flush();
614: }
615: }
616: } catch (IOException ioe) {
617: tryClose(true);
618: throw new SmbException(
619: "An error occured sending the request.", ioe);
620: } finally {
621: synchronized (outLock) {
622: releaseMid(mid);
623: }
624: }
625:
626: return;
627: }
628:
629: // now for the normal case where response is not null
630:
631: try {
632: synchronized (response) {
633: synchronized (outLock) {
634: response.received = false;
635: mid = aquireMid();
636: request.mid = mid.mid;
637: responseTable.put(mid, response);
638: ensureOpen();
639: synchronized (snd_buf) {
640: if (digest != null) {
641: request.digest = digest;
642: request.response = response;
643: }
644: int length = request
645: .writeWireFormat(snd_buf, 4);
646: out.write(snd_buf, 4, length);
647: out.flush();
648: }
649: }
650:
651: // default it 1 so that 0 can be used as forever
652: response
653: .wait(response.responseTimeout == 1 ? RESPONSE_TIMEOUT
654: : response.responseTimeout);
655: }
656: } catch (InterruptedException ie) {
657: tryClose(true);
658: } catch (IOException ioe) {
659: tryClose(true);
660: throw new SmbException(
661: "An error occured sending the request.", ioe);
662: } finally {
663: synchronized (outLock) {
664: responseTable.remove(mid);
665: releaseMid(mid);
666: }
667: }
668:
669: if (response.received == false) {
670: tryClose(true);
671: throw new SmbException(
672: "Timeout waiting for response from server: "
673: + address);
674: } else if (response.verifyFailed) {
675: tryClose(true);
676: throw new SmbException("Unverifiable signature: " + address);
677: }
678: response.errorCode = SmbException
679: .getStatusByCode(response.errorCode);
680: switch (response.errorCode) {
681: case NtStatus.NT_STATUS_OK:
682: break;
683: case NtStatus.NT_STATUS_ACCESS_DENIED:
684: case NtStatus.NT_STATUS_WRONG_PASSWORD:
685: case NtStatus.NT_STATUS_LOGON_FAILURE:
686: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
687: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
688: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
689: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
690: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
691: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
692: throw new SmbAuthException(response.errorCode);
693: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
694: if (request.auth == null) {
695: throw new SmbException(response.errorCode, null);
696: }
697: DfsReferral dr = getDfsReferral(request.auth, request.path);
698: referrals.add(dr);
699: throw dr;
700: default:
701: throw new SmbException(response.errorCode, null);
702: }
703: }
704:
705: void sendTransaction(SmbComTransaction request,
706: SmbComTransactionResponse response) throws SmbException {
707: Mid mid = null;
708:
709: negotiate();
710:
711: request.flags2 |= flags2;
712: request.useUnicode = useUnicode;
713: request.maxBufferSize = snd_buf_size;
714: response.received = false;
715: response.hasMore = true;
716: response.isPrimary = true;
717:
718: try {
719: request.txn_buf = BufferCache.getBuffer();
720: response.txn_buf = BufferCache.getBuffer();
721:
722: request.nextElement();
723: if (request.hasMoreElements()) {
724: // multi-part request
725:
726: SmbComBlankResponse interimResponse = new SmbComBlankResponse();
727:
728: synchronized (interimResponse) {
729: synchronized (outLock) {
730: mid = aquireMid();
731: request.mid = mid.mid;
732: responseTable.put(mid, interimResponse);
733: ensureOpen();
734: synchronized (snd_buf) {
735: request.digest = digest;
736: request.response = response;
737: int length = request.writeWireFormat(
738: snd_buf, 4);
739: out.write(snd_buf, 4, length);
740: out.flush();
741: }
742: }
743:
744: interimResponse.wait(RESPONSE_TIMEOUT);
745:
746: if (interimResponse.received == false) {
747: throw new SmbException(
748: "Timeout waiting for response from server: "
749: + address);
750: }
751: interimResponse.errorCode = SmbException
752: .getStatusByCode(interimResponse.errorCode);
753: switch (interimResponse.errorCode) {
754: case NtStatus.NT_STATUS_OK:
755: break;
756: case NtStatus.NT_STATUS_ACCESS_DENIED:
757: case NtStatus.NT_STATUS_WRONG_PASSWORD:
758: case NtStatus.NT_STATUS_LOGON_FAILURE:
759: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
760: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
761: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
762: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
763: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
764: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
765: throw new SmbAuthException(
766: interimResponse.errorCode);
767: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
768: if (request.auth == null) {
769: throw new SmbException(
770: interimResponse.errorCode, null);
771: }
772: DfsReferral dr = getDfsReferral(request.auth,
773: request.path);
774: referrals.add(dr);
775: throw dr;
776: default:
777: throw new SmbException(
778: interimResponse.errorCode, null);
779: }
780: }
781: request.nextElement();
782: }
783:
784: synchronized (response) {
785: synchronized (outLock) {
786: mid = aquireMid();
787: request.mid = mid.mid;
788: responseTable.put(mid, response);
789: }
790: do {
791: synchronized (outLock) {
792: ensureOpen();
793: synchronized (snd_buf) {
794: request.digest = digest;
795: request.response = response;
796: int length = request.writeWireFormat(
797: snd_buf, 4);
798: out.write(snd_buf, 4, length);
799: out.flush();
800: }
801: }
802: } while (request.hasMoreElements()
803: && request.nextElement() != null);
804:
805: do {
806: // default it 1 so that 0 can be used as forever
807: response.received = false;
808: response
809: .wait(response.responseTimeout == 1 ? RESPONSE_TIMEOUT
810: : response.responseTimeout);
811: } while (response.received
812: && response.hasMoreElements());
813: }
814: } catch (InterruptedException ie) {
815: tryClose(true);
816: } catch (IOException ioe) {
817: tryClose(true);
818: throw new SmbException(
819: "An error occured sending the request.", ioe);
820: } finally {
821: synchronized (outLock) {
822: responseTable.remove(mid);
823: releaseMid(mid);
824: }
825: BufferCache.releaseBuffer(request.txn_buf);
826: BufferCache.releaseBuffer(response.txn_buf);
827: }
828:
829: if (response.received == false) {
830: tryClose(true);
831: throw new SmbException(
832: "Timeout waiting for response from server: "
833: + address);
834: } else if (response.verifyFailed) {
835: tryClose(true);
836: throw new SmbException("Unverifiable signature: " + address);
837: }
838: response.errorCode = SmbException
839: .getStatusByCode(response.errorCode);
840: switch (response.errorCode) {
841: case NtStatus.NT_STATUS_OK:
842: break;
843: case NtStatus.NT_STATUS_ACCESS_DENIED:
844: case NtStatus.NT_STATUS_WRONG_PASSWORD:
845: case NtStatus.NT_STATUS_LOGON_FAILURE:
846: case NtStatus.NT_STATUS_ACCOUNT_RESTRICTION:
847: case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
848: case NtStatus.NT_STATUS_INVALID_WORKSTATION:
849: case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
850: case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
851: case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
852: throw new SmbAuthException(response.errorCode);
853: case NtStatus.NT_STATUS_PATH_NOT_COVERED:
854: if (request.auth == null) {
855: throw new SmbException(response.errorCode, null);
856: }
857: DfsReferral dr = getDfsReferral(request.auth, request.path);
858: referrals.add(dr);
859: throw dr;
860: default:
861: throw new SmbException(response.errorCode, null);
862: }
863: }
864:
865: synchronized void negotiate() throws SmbException {
866:
867: if (this == NULL_TRANSPORT) {
868: throw new RuntimeException("Null transport cannot be used");
869: }
870: if (state >= ST_NEGOTIATING) {
871: return;
872: }
873: state = ST_NEGOTIATING;
874:
875: if (DebugFile.trace)
876: DebugFile.writeln("requesting negotiation with " + address);
877:
878: /*
879: * Negotiate Protocol Request / Response
880: */
881:
882: SmbComNegotiateResponse response = new SmbComNegotiateResponse();
883: send(new SmbComNegotiate(), response);
884:
885: if (response.dialectIndex > 10) {
886: tryClose(true);
887: throw new SmbException(
888: "This client does not support the negotiated dialect.");
889: }
890:
891: server = new ServerData();
892: server.securityMode = response.securityMode;
893: server.security = response.security;
894: server.encryptedPasswords = response.encryptedPasswords;
895: server.signaturesEnabled = response.signaturesEnabled;
896: server.signaturesRequired = response.signaturesRequired;
897: server.maxMpxCount = response.maxMpxCount;
898: server.maxNumberVcs = response.maxNumberVcs;
899: server.maxBufferSize = response.maxBufferSize;
900: server.maxRawSize = response.maxRawSize;
901: server.sessionKey = response.sessionKey;
902: server.capabilities = response.capabilities;
903: server.serverTime = response.serverTime;
904: server.serverTimeZone = response.serverTimeZone;
905: server.encryptionKeyLength = response.encryptionKeyLength;
906: server.encryptionKey = response.encryptionKey;
907: server.oemDomainName = response.oemDomainName;
908:
909: if (server.signaturesRequired
910: || (server.signaturesEnabled && SIGNPREF)) {
911: flags2 |= ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
912: } else {
913: flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES;
914: }
915:
916: maxMpxCount = maxMpxCount < server.maxMpxCount ? maxMpxCount
917: : server.maxMpxCount;
918: maxMpxCount = maxMpxCount < 1 ? 1 : maxMpxCount;
919:
920: snd_buf_size = snd_buf_size < server.maxBufferSize ? snd_buf_size
921: : server.maxBufferSize;
922:
923: capabilities &= server.capabilities;
924: if ((capabilities & ServerMessageBlock.CAP_UNICODE) == 0) {
925: // server doesn't want unicode
926: if (FORCE_UNICODE) {
927: capabilities |= ServerMessageBlock.CAP_UNICODE;
928: } else {
929: useUnicode = false;
930: flags2 &= 0xFFFF ^ ServerMessageBlock.FLAGS2_UNICODE;
931: }
932: }
933: }
934:
935: public String toString() {
936: String ret = "SmbTransport[address=" + address;
937: if (socket == null) {
938: ret += ",port=,localAddr=,localPort=]";
939: } else {
940: ret += ",port=" + socket.getPort() + ",localAddr="
941: + socket.getLocalAddress() + ",localPort="
942: + socket.getLocalPort() + "]";
943: }
944: return ret;
945: }
946:
947: /* Manage MIDs */
948:
949: Mid aquireMid() throws SmbException {
950: int i;
951:
952: if (mid_next == 0) {
953: mid_next++;
954: }
955:
956: for (;;) {
957: for (i = 0; i < maxMpxCount; i++) {
958: if (mids[i].mid == 0) {
959: break;
960: }
961: }
962: if (i == maxMpxCount) {
963: try {
964: outLock.wait();
965: } catch (InterruptedException ie) {
966: throw new SmbException("Interrupted aquiring mid",
967: ie);
968: }
969: } else {
970: break;
971: }
972: }
973:
974: mids[i].mid = mid_next++;
975:
976: return mids[i];
977: }
978:
979: void releaseMid(Mid mid) {
980: int i;
981:
982: for (i = 0; i < maxMpxCount; i++) {
983: if (mids[i].mid == mid.mid) {
984: mid.mid = 0;
985: outLock.notify();
986: return;
987: }
988: }
989: if (DebugFile.trace)
990: DebugFile.writeln("mid not found");
991: }
992: }
|