+jcifs-1.3.13
+
+Locking throughout the transport layer has been rewritten. This should
+fix the long standing deadlock that has been reported in the past. Doubled
+size of transient input buffer to accommodate SMB_COM_NEGOTIATE response
+security blob (as observed with OSX Snow Leopard). A signing issue reading
+data from an EMC server has been fixed. NTLMSSP logging has been improved.
+
Fri Aug 14 13:45:57 EDT 2009
jcifs-1.3.12
<project name="jcifs" default="usage" basedir=".">
- <property name="version" value="1.3.12"/>
- <property name="reldate" value="Aug 14, 2009"/>
+ <property name="version" value="1.3.13"/>
+ <property name="reldate" value="Jan 5, 2010"/>
<target name="usage">
<echo>
SHARE=root2
DIR=test
-# smb://fs1.w.net/DFSStandaloneRoot/DFSStandaloneLink/test/
+# smb://fs4.w.net/DFSStandaloneRoot/DFSStandaloneLink/test/
# smb://dc1.w.net/root2/link2/test/
# smb://dc1.w.net/tmp/test/
# smb://dc3.x.net/tmp/test/
--- /dev/null
+--- jcifs_1.3.12/src/jcifs/smb/SmbFile.java
++++ jcifs/src/jcifs/smb/SmbFile.java
+@@ -2564,6 +2564,21 @@
+ attrExpiration = 0;
+ }
+
++ void setPathInformation( int attrs, long ctime, long atime, long mtime ) throws SmbException {
++ int f, dir;
++
++ exists();
++ dir = attributes & ATTR_DIRECTORY;
++
++ f = open0( O_RDONLY, FILE_WRITE_ATTRIBUTES,
++ dir, dir != 0 ? 0x0001 : 0x0040 );
++ send( new Trans2SetFileInformation( f, attrs | dir, ctime, atime, mtime ),
++ new Trans2SetFileInformationResponse() );
++ close( f, 0L );
++
++ attrExpiration = 0;
++ }
++
+ /**
+ * Set the create time of the file. The time is specified as milliseconds
+ * from Jan 1, 1970 which is the same as that which is returned by the
+@@ -2581,6 +2596,22 @@
+ setPathInformation( 0, time, 0L );
+ }
+ /**
++ * Set the access time of the file. The time is specified as milliseconds
++ * from Jan 1, 1970 which is the same as that which is returned by the
++ * <tt>createTime()</tt> method.
++ * <p/>
++ * This method does not apply to workgroups, servers, or shares.
++ *
++ * @param time the create time as milliseconds since Jan 1, 1970
++ */
++ public void setAccessTime( long time ) throws SmbException {
++ if( getUncPath0().length() == 1 ) {
++ throw new SmbException( "Invalid operation for workgroups, servers, or shares" );
++ }
++
++ setPathInformation( 0, 0L, time, 0L );
++ }
++/**
+ * Set the last modified time of the file. The time is specified as milliseconds
+ * from Jan 1, 1970 which is the same as that which is returned by the
+ * <tt>lastModified()</tt>, <tt>getLastModified()</tt>, and <tt>getDate()</tt> methods.
+--- jcifs_1.3.12/src/jcifs/smb/Trans2SetFileInformation.java
++++ jcifs/src/jcifs/smb/Trans2SetFileInformation.java
+@@ -25,6 +25,7 @@
+ private int fid;
+ private int attributes;
+ private long createTime, lastWriteTime;
++ private long accessTime;
+
+ Trans2SetFileInformation( int fid, int attributes, long createTime, long lastWriteTime ) {
+ this.fid = fid;
+@@ -38,6 +39,19 @@
+ maxSetupCount = (byte)0x00;
+ }
+
++ Trans2SetFileInformation( int fid, int attributes, long createTime, long accessTime, long lastWriteTime ) {
++ this.fid = fid;
++ this.attributes = attributes;
++ this.accessTime = accessTime;
++ this.createTime = createTime;
++ this.lastWriteTime = lastWriteTime;
++ command = SMB_COM_TRANSACTION2;
++ subCommand = TRANS2_SET_FILE_INFORMATION;
++ maxParameterCount = 6;
++ maxDataCount = 0;
++ maxSetupCount = (byte)0x00;
++ }
++
+ int writeSetupWireFormat( byte[] dst, int dstIndex ) {
+ dst[dstIndex++] = subCommand;
+ dst[dstIndex++] = (byte)0x00;
+@@ -58,13 +72,22 @@
+ int writeDataWireFormat( byte[] dst, int dstIndex ) {
+ int start = dstIndex;
+
++ // create time
+ writeTime( createTime, dst, dstIndex ); dstIndex += 8;
+- writeInt8( 0L, dst, dstIndex ); dstIndex += 8;
++
++ // access time
++ writeInt8( accessTime, dst, dstIndex ); dstIndex += 8;
++
++ // last write time [modification]
+ writeTime( lastWriteTime, dst, dstIndex ); dstIndex += 8;
++
++ // change time
+ writeInt8( 0L, dst, dstIndex ); dstIndex += 8;
++
+ /* Samba 2.2.7 needs ATTR_NORMAL
+ */
+ writeInt2( 0x80 | attributes, dst, dstIndex ); dstIndex += 2;
++
+ /* 6 zeros observed with NT */
+ writeInt8( 0L, dst, dstIndex ); dstIndex += 6;
+
isDirect = msg instanceof DcerpcBind;
- try {
- stub = jcifs.smb.BufferCache.getBuffer();
- } catch (InterruptedException ie) {
- throw new IOException(ie.getMessage());
- }
+ stub = jcifs.smb.BufferCache.getBuffer();
try {
int off, tot, n;
public String toString() {
String suppliedDomain = getSuppliedDomain();
String suppliedWorkstation = getSuppliedWorkstation();
- int flags = getFlags();
- StringBuffer buffer = new StringBuffer();
- if (suppliedDomain != null) {
- buffer.append("suppliedDomain: ").append(suppliedDomain);
- }
- if (suppliedWorkstation != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("suppliedWorkstation: ").append(suppliedWorkstation);
- }
- if (flags != 0) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("flags: ");
- buffer.append("0x");
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));
- buffer.append(Integer.toHexString(flags & 0x0f));
- }
- return buffer.toString();
+ return "Type1Message[suppliedDomain=" + (suppliedDomain == null ? "null" : suppliedDomain) +
+ ",suppliedWorkstation=" + (suppliedWorkstation == null ? "null" : suppliedWorkstation) +
+ ",flags=0x" + jcifs.util.Hexdump.toHexString(getFlags(), 8) + "]";
}
/**
byte[] challenge = getChallenge();
byte[] context = getContext();
byte[] targetInformation = getTargetInformation();
- int flags = getFlags();
- StringBuffer buffer = new StringBuffer();
- if (target != null) {
- buffer.append("target: ").append(target);
- }
- if (challenge != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("challenge: ");
- buffer.append("0x");
- for (int i = 0; i < challenge.length; i++) {
- buffer.append(Integer.toHexString((challenge[i] >> 4) & 0x0f));
- buffer.append(Integer.toHexString(challenge[i] & 0x0f));
- }
- }
- if (context != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("context: ");
- buffer.append("0x");
- for (int i = 0; i < context.length; i++) {
- buffer.append(Integer.toHexString((context[i] >> 4) & 0x0f));
- buffer.append(Integer.toHexString(context[i] & 0x0f));
- }
- }
- if (targetInformation != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("targetInformation: ");
- buffer.append("0x");
- for (int i = 0; i < targetInformation.length; i++) {
- buffer.append(Integer.toHexString((targetInformation[i] >> 4) &
- 0x0f));
- buffer.append(Integer.toHexString(targetInformation[i] & 0x0f));
- }
- }
- if (flags != 0) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("flags: ");
- buffer.append("0x");
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));
- buffer.append(Integer.toHexString(flags & 0x0f));
- }
- return buffer.toString();
+
+ return "Type2Message[target=" + target +
+ ",challenge=" + (challenge == null ? "null" : "<" + challenge.length + " bytes>") +
+ ",context=" + (context == null ? "null" : "<" + context.length + " bytes>") +
+ ",targetInformation=" + (targetInformation == null ? "null" : "<" + targetInformation.length + " bytes>") +
+ ",flags=0x" + jcifs.util.Hexdump.toHexString(getFlags(), 8) + "]";
}
/**
byte[] lmResponse = getLMResponse();
byte[] ntResponse = getNTResponse();
byte[] sessionKey = getSessionKey();
- int flags = getFlags();
- StringBuffer buffer = new StringBuffer();
- if (domain != null) {
- buffer.append("domain: ").append(domain);
- }
- if (user != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("user: ").append(user);
- }
- if (workstation != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("workstation: ").append(workstation);
- }
- if (lmResponse != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("lmResponse: ");
- buffer.append("0x");
- for (int i = 0; i < lmResponse.length; i++) {
- buffer.append(Integer.toHexString((lmResponse[i] >> 4) & 0x0f));
- buffer.append(Integer.toHexString(lmResponse[i] & 0x0f));
- }
- }
- if (ntResponse != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("ntResponse: ");
- buffer.append("0x");
- for (int i = 0; i < ntResponse.length; i++) {
- buffer.append(Integer.toHexString((ntResponse[i] >> 4) & 0x0f));
- buffer.append(Integer.toHexString(ntResponse[i] & 0x0f));
- }
- }
- if (sessionKey != null) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("sessionKey: ");
- buffer.append("0x");
- for (int i = 0; i < sessionKey.length; i++) {
- buffer.append(Integer.toHexString((sessionKey[i] >> 4) & 0x0f));
- buffer.append(Integer.toHexString(sessionKey[i] & 0x0f));
- }
- }
- if (flags != 0) {
- if (buffer.length() > 0) buffer.append("; ");
- buffer.append("flags: ");
- buffer.append("0x");
- buffer.append(Integer.toHexString((flags >> 28) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 24) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 20) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 16) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 12) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 8) & 0x0f));
- buffer.append(Integer.toHexString((flags >> 4) & 0x0f));
- buffer.append(Integer.toHexString(flags & 0x0f));
- }
- return buffer.toString();
+
+ return "Type3Message[domain=" + domain +
+ ",user=" + user +
+ ",workstation=" + workstation +
+ ",lmResponse=" + (lmResponse == null ? "null" : "<" + lmResponse.length + " bytes>") +
+ ",ntResponse=" + (ntResponse == null ? "null" : "<" + ntResponse.length + " bytes>") +
+ ",sessionKey=" + (sessionKey == null ? "null" : "<" + sessionKey.length + " bytes>") +
+ ",flags=0x" + jcifs.util.Hexdump.toHexString(getFlags(), 8) + "]";
}
/**
--- /dev/null
+/* jcifs smb client library in Java
+ * Copyright (C) 2000 "Michael B. Allen" <jcifs at samba dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package jcifs.smb;
+
+import jcifs.Config;
+
+public class BufferCache {
+
+ private static final int MAX_BUFFERS = Config.getInt( "jcifs.smb.maxBuffers", 16 );
+
+ static Object[] cache = new Object[MAX_BUFFERS];
+ private static int freeBuffers = 0;
+
+ private static byte[] getBuffer0() {
+ byte[] buf;
+
+ if (freeBuffers > 0) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ if( cache[i] != null ) {
+ buf = (byte[])cache[i];
+ cache[i] = null;
+ freeBuffers--;
+ return buf;
+ }
+ }
+ }
+
+ buf = new byte[SmbComTransaction.TRANSACTION_BUF_SIZE];
+
+ return buf;
+ }
+
+ static void getBuffers( SmbComTransaction req,
+ SmbComTransactionResponse rsp ) throws InterruptedException {
+ synchronized( cache ) {
+ if (freeBuffers < 2) {
+ /* The first time this is called we always wait because freeBuffers
+ * will be 0. But after a few calls to releaseBuffer, threads will
+ * no longer wait.
+ */
+ cache.wait(100);
+ }
+ req.txn_buf = getBuffer0();
+ rsp.txn_buf = getBuffer0();
+ }
+ }
+ static public byte[] getBuffer() throws InterruptedException {
+ synchronized( cache ) {
+ if (freeBuffers < 1) {
+ cache.wait(100);
+ }
+ return getBuffer0();
+ }
+ }
+ static public void releaseBuffer( byte[] buf ) {
+ synchronized( cache ) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ if (cache[i] == null) {
+ cache[i] = buf;
+ freeBuffers++;
+ cache.notify();
+ return;
+ }
+ }
+ }
+ }
+}
private static final int MAX_BUFFERS = Config.getInt( "jcifs.smb.maxBuffers", 16 );
static Object[] cache = new Object[MAX_BUFFERS];
- private static int numBuffers = 0;
private static int freeBuffers = 0;
- private static byte[] getBuffer0() {
- byte[] buf;
+ static public byte[] getBuffer() {
+ synchronized( cache ) {
+ byte[] buf;
- if (freeBuffers > 0) {
- for (int i = 0; i < MAX_BUFFERS; i++) {
- if( cache[i] != null ) {
- buf = (byte[])cache[i];
- cache[i] = null;
- freeBuffers--;
- return buf;
+ if (freeBuffers > 0) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ if( cache[i] != null ) {
+ buf = (byte[])cache[i];
+ cache[i] = null;
+ freeBuffers--;
+ return buf;
+ }
}
}
- }
- buf = new byte[SmbComTransaction.TRANSACTION_BUF_SIZE];
- numBuffers++;
-
- return buf;
- }
+ buf = new byte[SmbComTransaction.TRANSACTION_BUF_SIZE];
- static void getBuffers( SmbComTransaction req,
- SmbComTransactionResponse rsp ) throws InterruptedException {
- synchronized( cache ) {
- while ((freeBuffers + (MAX_BUFFERS - numBuffers)) < 2) {
- cache.wait();
- }
- req.txn_buf = getBuffer0();
- rsp.txn_buf = getBuffer0();
+ return buf;
}
}
- static public byte[] getBuffer() throws InterruptedException {
+ static void getBuffers( SmbComTransaction req, SmbComTransactionResponse rsp ) {
synchronized( cache ) {
- while ((freeBuffers + (MAX_BUFFERS - numBuffers)) < 1) {
- cache.wait();
- }
- return getBuffer0();
+ req.txn_buf = getBuffer();
+ rsp.txn_buf = getBuffer();
}
}
static public void releaseBuffer( byte[] buf ) {
synchronized( cache ) {
- for (int i = 0; i < MAX_BUFFERS; i++) {
- if (cache[i] == null) {
- cache[i] = buf;
- freeBuffers++;
- cache.notify();
- return;
+ if (freeBuffers < MAX_BUFFERS) {
+ for (int i = 0; i < MAX_BUFFERS; i++) {
+ if (cache[i] == null) {
+ cache[i] = buf;
+ freeBuffers++;
+ return;
+ }
}
}
}
import java.io.IOException;
import java.security.*;
import jcifs.ntlmssp.*;
+import jcifs.util.LogStream;
+import jcifs.util.Hexdump;
/**
For initiating NTLM authentication (including NTLMv2). If you want to add NTLMv2 authentication support to something this is what you want to use. See the code for details. Note that JCIFS does not implement the acceptor side of NTLM authentication.
public class NtlmContext {
+
NtlmPasswordAuthentication auth;
int ntlmsspFlags;
String workstation;
byte[] serverChallenge = null;
byte[] signingKey = null;
int state = 1;
+ LogStream log;
public NtlmContext(NtlmPasswordAuthentication auth, boolean doSigning) {
this.auth = auth;
NtlmFlags.NTLMSSP_NEGOTIATE_KEY_EXCH;
}
this.workstation = Type1Message.getDefaultWorkstation();
+ log = LogStream.getInstance();
+ }
+
+ public String toString() {
+ String ret = "NtlmContext[auth=" + auth +
+ ",ntlmsspFlags=0x" + Hexdump.toHexString(ntlmsspFlags, 8) +
+ ",workstation=" + workstation +
+ ",isEstablished=" + isEstablished +
+ ",state=" + state +
+ ",serverChallenge=";
+ if (serverChallenge == null) {
+ ret += "null";
+ } else {
+ ret += Hexdump.toHexString(serverChallenge, 0, serverChallenge.length * 2);
+ }
+ ret += ",signingKey=";
+ if (signingKey == null) {
+ ret += "null";
+ } else {
+ ret += Hexdump.toHexString(signingKey, 0, signingKey.length * 2);
+ }
+ ret += "]";
+ return ret;
}
public boolean isEstablished() {
case 1:
Type1Message msg1 = new Type1Message(ntlmsspFlags, auth.getDomain(), workstation);
token = msg1.toByteArray();
+
+ if (log.level >= 4) {
+ log.println(msg1);
+ if (log.level >= 6)
+ Hexdump.hexdump(log, token, 0, token.length);
+ }
+
state++;
break;
case 2:
try {
Type2Message msg2 = new Type2Message(token);
+ if (log.level >= 4) {
+ log.println(msg2);
+ if (log.level >= 6)
+ Hexdump.hexdump(log, token, 0, token.length);
+ }
+
serverChallenge = msg2.getChallenge();
ntlmsspFlags &= msg2.getFlags();
ntlmsspFlags);
token = msg3.toByteArray();
+ if (log.level >= 4) {
+ log.println(msg3);
+ if (log.level >= 6)
+ Hexdump.hexdump(log, token, 0, token.length);
+ }
+
if ((ntlmsspFlags & NtlmFlags.NTLMSSP_NEGOTIATE_SIGN) != 0)
signingKey = msg3.getMasterKey();
protected static Dfs dfs;
NtlmPasswordAuthentication auth; // Cannot be null
- SmbTree tree = null; // Initially null; may be !tree.treeConnected
+ SmbTree tree = null; // Initially null
String unc; // Initially null; set by getUncPath; never ends with '/'
int fid; // Initially 0; set by open()
int type;
String hostName = getServerWithDfs();
tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, auth) != null;
if (tree.inDomainDfs) {
- tree.treeConnected = true;
+ tree.connectionState = 2;
}
try {
tree = ssn.getSmbTree(share, null);
tree.inDomainDfs = dfs.resolve(hostName, tree.share, null, auth) != null;
if (tree.inDomainDfs) {
- tree.treeConnected = true;
+ tree.connectionState = 2;
}
tree.treeConnect(null, null);
} else {
}
}
boolean isConnected() {
- return tree != null && tree.treeConnected;
+ return tree != null && tree.connectionState == 2;
}
int open0( int flags, int access, int attrs, int options ) throws SmbException {
int f;
tree_num = tree.tree_num;
}
boolean isOpen() {
- boolean ans = opened && isConnected() && tree_num == tree.tree_num;
+ boolean ans = opened && isConnected() && tree_num == tree.tree_num;
return ans;
}
void close( int f, long lastWriteTime ) throws SmbException {
if( DOMAIN == null ) {
throw new SmbException( "A domain was not specified" );
}
- synchronized (DOMAIN) {
+synchronized (DOMAIN) {
long now = System.currentTimeMillis();
-int retry = 1;
-
-do {
- if (dc_list_expiration < now) {
- NbtAddress[] list = NbtAddress.getAllByName( DOMAIN, 0x1C, null, null );
- dc_list_expiration = now + CACHE_POLICY * 1000L;
- if (list != null && list.length > 0) {
- dc_list = list;
- } else { /* keep using the old list */
- dc_list_expiration = now + 1000 * 60 * 15; /* 15 min */
- if (SmbTransport.log.level >= 2) {
- SmbTransport.log.println( "Failed to retrieve DC list from WINS" );
+ int retry = 1;
+
+ do {
+ if (dc_list_expiration < now) {
+ NbtAddress[] list = NbtAddress.getAllByName( DOMAIN, 0x1C, null, null );
+ dc_list_expiration = now + CACHE_POLICY * 1000L;
+ if (list != null && list.length > 0) {
+ dc_list = list;
+ } else { /* keep using the old list */
+ dc_list_expiration = now + 1000 * 60 * 15; /* 15 min */
+ if (SmbTransport.log.level >= 2) {
+ SmbTransport.log.println( "Failed to retrieve DC list from WINS" );
+ }
}
}
- }
- int max = Math.min( dc_list.length, LOOKUP_RESP_LIMIT );
- for (int j = 0; j < max; j++) {
- int i = dc_list_counter++ % max;
- if (dc_list[i] != null) {
- try {
- return interrogate( dc_list[i] );
- } catch (SmbException se) {
- if (SmbTransport.log.level >= 2) {
- SmbTransport.log.println( "Failed validate DC: " + dc_list[i] );
- if (SmbTransport.log.level > 2)
- se.printStackTrace( SmbTransport.log );
+ int max = Math.min( dc_list.length, LOOKUP_RESP_LIMIT );
+ for (int j = 0; j < max; j++) {
+ int i = dc_list_counter++ % max;
+ if (dc_list[i] != null) {
+ try {
+ return interrogate( dc_list[i] );
+ } catch (SmbException se) {
+ if (SmbTransport.log.level >= 2) {
+ SmbTransport.log.println( "Failed validate DC: " + dc_list[i] );
+ if (SmbTransport.log.level > 2)
+ se.printStackTrace( SmbTransport.log );
+ }
}
+ dc_list[i] = null;
}
- dc_list[i] = null;
}
- }
-/* No DCs found, for retieval of list by expiring it and retry.
- */
- dc_list_expiration = 0;
-} while (retry-- > 0);
+ /* No DCs found, for retieval of list by expiring it and retry.
+ */
+ dc_list_expiration = 0;
+ } while (retry-- > 0);
dc_list_expiration = now + 1000 * 60 * 15; /* 15 min */
- }
+}
throw new UnknownHostException(
"Failed to negotiate with a suitable domain controller for " + DOMAIN );
}
}
- private int uid;
+ /* 0 - not connected
+ * 1 - connecting
+ * 2 - connected
+ * 3 - disconnecting
+ */
+ int connectionState;
+ int uid;
Vector trees;
- private boolean sessionSetup;
// Transport parameters allows trans to be removed from CONNECTIONS
private UniAddress address;
private int port, localPort;
this.localPort = localPort;
this.auth = auth;
trees = new Vector();
+ connectionState = 0;
}
synchronized SmbTree getSmbTree( String share, String service ) {
}
void send( ServerMessageBlock request,
ServerMessageBlock response ) throws SmbException {
+synchronized (transport()) {
if( response != null ) {
response.received = false;
}
- synchronized(transport.setupDiscoLock) {
- expiration = System.currentTimeMillis() + SmbTransport.SO_TIMEOUT;
- sessionSetup( request, response );
- if( response != null && response.received ) {
- return;
- }
- request.uid = uid;
- request.auth = auth;
- try {
- transport.send( request, response );
- } catch (SmbException se) {
- if (request instanceof SmbComTreeConnectAndX) {
- logoff(true);
- }
- request.digest = null;
- throw se;
+ expiration = System.currentTimeMillis() + SmbTransport.SO_TIMEOUT;
+ sessionSetup( request, response );
+ if( response != null && response.received ) {
+ return;
+ }
+ request.uid = uid;
+ request.auth = auth;
+ try {
+ transport.send( request, response );
+ } catch (SmbException se) {
+ if (request instanceof SmbComTreeConnectAndX) {
+ logoff(true);
}
+ request.digest = null;
+ throw se;
}
+}
}
void sessionSetup( ServerMessageBlock andx,
- ServerMessageBlock andxResponse ) throws SmbException {
+ ServerMessageBlock andxResponse ) throws SmbException {
+synchronized (transport()) {
NtlmContext nctx = null;
SmbException ex = null;
SmbComSessionSetupAndX request;
byte[] token = new byte[0];
int state = 10;
-synchronized( transport() ) {
- if( sessionSetup ) {
- return;
+ while (connectionState != 0) {
+ if (connectionState == 2 || connectionState == 3) // connected or disconnecting
+ return;
+ try {
+ transport.wait();
+ } catch (InterruptedException ie) {
+ throw new SmbException(ie.getMessage(), ie);
+ }
}
+ connectionState = 1; // trying ...
- transport.connect();
-
- /*
- * Session Setup And X Request / Response
- */
-
- if( transport.log.level >= 4 )
- transport.log.println( "sessionSetup: accountName=" + auth.username + ",primaryDomain=" + auth.domain );
-
- /* We explicitly set uid to 0 here to prevent a new
- * SMB_COM_SESSION_SETUP_ANDX from having it's uid set to an
- * old value when the session is re-established. Otherwise a
- * "The parameter is incorrect" error can occur.
- */
- uid = 0;
-
- do {
- switch (state) {
- case 10: /* NTLM */
- if (auth != NtlmPasswordAuthentication.ANONYMOUS &&
- transport.hasCapability(SmbConstants.CAP_EXTENDED_SECURITY)) {
- state = 20; /* NTLMSSP */
- break;
- }
-
- request = new SmbComSessionSetupAndX( this, andx, auth );
- response = new SmbComSessionSetupAndXResponse( andxResponse );
+ try {
+ transport.connect();
- /* Create SMB signature digest if necessary
- * Only the first SMB_COM_SESSION_SETUP_ANX with non-null or
- * blank password initializes signing.
- */
- if (transport.isSignatureSetupRequired( auth )) {
- if( auth.hashesExternal && NtlmPasswordAuthentication.DEFAULT_PASSWORD != NtlmPasswordAuthentication.BLANK ) {
- /* preauthentication
- */
- transport.getSmbSession( NtlmPasswordAuthentication.DEFAULT ).getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
- } else {
- byte[] signingKey = auth.getSigningKey(transport.server.encryptionKey);
- request.digest = new SigningDigest(signingKey, false);
+ /*
+ * Session Setup And X Request / Response
+ */
+
+ if( transport.log.level >= 4 )
+ transport.log.println( "sessionSetup: accountName=" + auth.username + ",primaryDomain=" + auth.domain );
+
+ /* We explicitly set uid to 0 here to prevent a new
+ * SMB_COM_SESSION_SETUP_ANDX from having it's uid set to an
+ * old value when the session is re-established. Otherwise a
+ * "The parameter is incorrect" error can occur.
+ */
+ uid = 0;
+
+ do {
+ switch (state) {
+ case 10: /* NTLM */
+ if (auth != NtlmPasswordAuthentication.ANONYMOUS &&
+ transport.hasCapability(SmbConstants.CAP_EXTENDED_SECURITY)) {
+ state = 20; /* NTLMSSP */
+ break;
}
- }
-
- request.auth = auth;
-
- try {
- transport.send( request, response );
- } catch (SmbAuthException sae) {
- throw sae;
- } catch (SmbException se) {
- ex = se;
- }
-
- if( response.isLoggedInAsGuest &&
- "GUEST".equalsIgnoreCase( auth.username ) == false &&
- transport.server.security != SmbConstants.SECURITY_SHARE) {
- throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
- }
-
- if (ex != null)
- throw ex;
-
- uid = response.uid;
-
- if( request.digest != null ) {
- /* success - install the signing digest */
- transport.digest = request.digest;
- }
-
- sessionSetup = true;
- state = 0;
-
- break;
- case 20:
- if (nctx == null) {
- boolean doSigning = (transport.flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES) != 0;
- nctx = new NtlmContext(auth, doSigning);
- }
-
- if (nctx.isEstablished()) {
- sessionSetup = true;
- state = 0;
- break;
- }
-
- try {
- token = nctx.initSecContext(token, 0, token.length);
- } catch (SmbException se) {
- /* We must close the transport or the server will be expecting a
- * Type3Message. Otherwise, when we send a Type1Message it will return
- * "Invalid parameter".
+
+ request = new SmbComSessionSetupAndX( this, andx, auth );
+ response = new SmbComSessionSetupAndXResponse( andxResponse );
+
+ /* Create SMB signature digest if necessary
+ * Only the first SMB_COM_SESSION_SETUP_ANX with non-null or
+ * blank password initializes signing.
*/
- try { transport.disconnect(true); } catch (IOException ioe) {}
- uid = 0;
- throw se;
- }
-
- if (token != null) {
- request = new SmbComSessionSetupAndX(this, null, token);
- response = new SmbComSessionSetupAndXResponse(null);
-
if (transport.isSignatureSetupRequired( auth )) {
- byte[] signingKey = nctx.getSigningKey();
- if (signingKey != null)
- request.digest = new SigningDigest(signingKey, true);
+ if( auth.hashesExternal && NtlmPasswordAuthentication.DEFAULT_PASSWORD != NtlmPasswordAuthentication.BLANK ) {
+ /* preauthentication
+ */
+ transport.getSmbSession( NtlmPasswordAuthentication.DEFAULT ).getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
+ } else {
+ byte[] signingKey = auth.getSigningKey(transport.server.encryptionKey);
+ request.digest = new SigningDigest(signingKey, false);
+ }
}
-
- request.uid = uid;
- uid = 0;
-
+
+ request.auth = auth;
+
try {
transport.send( request, response );
} catch (SmbAuthException sae) {
throw sae;
} catch (SmbException se) {
ex = se;
- /* Apparently once a successfull NTLMSSP login occurs, the
- * server will return "Access denied" even if a logoff is
- * sent. Unfortunately calling disconnect() doesn't always
- * actually shutdown the connection before other threads
- * have committed themselves (e.g. InterruptTest example).
- */
- try { transport.disconnect(true); } catch (Exception e) {}
}
-
+
if( response.isLoggedInAsGuest &&
- "GUEST".equalsIgnoreCase( auth.username ) == false) {
+ "GUEST".equalsIgnoreCase( auth.username ) == false &&
+ transport.server.security != SmbConstants.SECURITY_SHARE) {
throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
}
-
+
if (ex != null)
throw ex;
-
+
uid = response.uid;
-
- if (request.digest != null) {
+
+ if( request.digest != null ) {
/* success - install the signing digest */
transport.digest = request.digest;
}
+
+ connectionState = 2;
- token = response.blob;
- }
+ state = 0;
+
+ break;
+ case 20:
+ if (nctx == null) {
+ boolean doSigning = (transport.flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES) != 0;
+ nctx = new NtlmContext(auth, doSigning);
+ }
+
+ if (SmbTransport.log.level >= 4)
+ SmbTransport.log.println(nctx);
+
+ if (nctx.isEstablished()) {
- break;
- default:
- throw new SmbException("Unexpected session setup state: " + state);
- }
- } while (state != 0);
+ connectionState = 2;
+
+ state = 0;
+ break;
+ }
+
+ try {
+ token = nctx.initSecContext(token, 0, token.length);
+ } catch (SmbException se) {
+ /* We must close the transport or the server will be expecting a
+ * Type3Message. Otherwise, when we send a Type1Message it will return
+ * "Invalid parameter".
+ */
+ try { transport.disconnect(true); } catch (IOException ioe) {}
+ uid = 0;
+ throw se;
+ }
+
+ if (token != null) {
+ request = new SmbComSessionSetupAndX(this, null, token);
+ response = new SmbComSessionSetupAndXResponse(null);
+
+ if (transport.isSignatureSetupRequired( auth )) {
+ byte[] signingKey = nctx.getSigningKey();
+ if (signingKey != null)
+ request.digest = new SigningDigest(signingKey, true);
+ }
+
+ request.uid = uid;
+ uid = 0;
+
+ try {
+ transport.send( request, response );
+ } catch (SmbAuthException sae) {
+ throw sae;
+ } catch (SmbException se) {
+ ex = se;
+ /* Apparently once a successfull NTLMSSP login occurs, the
+ * server will return "Access denied" even if a logoff is
+ * sent. Unfortunately calling disconnect() doesn't always
+ * actually shutdown the connection before other threads
+ * have committed themselves (e.g. InterruptTest example).
+ */
+ try { transport.disconnect(true); } catch (Exception e) {}
+ }
+
+ if( response.isLoggedInAsGuest &&
+ "GUEST".equalsIgnoreCase( auth.username ) == false) {
+ throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
+ }
+
+ if (ex != null)
+ throw ex;
+
+ uid = response.uid;
+
+ if (request.digest != null) {
+ /* success - install the signing digest */
+ transport.digest = request.digest;
+ }
+
+ token = response.blob;
+ }
+
+ break;
+ default:
+ throw new SmbException("Unexpected session setup state: " + state);
+ }
+ } while (state != 0);
+ } catch (SmbException se) {
+ logoff(true);
+ connectionState = 0;
+ throw se;
+ } finally {
+ transport.notifyAll();
+ }
}
}
void logoff( boolean inError ) {
-synchronized( transport() ) {
- if( sessionSetup == false ) {
+synchronized (transport()) {
+
+ if (connectionState != 2) // not-connected
return;
- }
+ connectionState = 3; // disconnecting
for( Enumeration e = trees.elements(); e.hasMoreElements(); ) {
SmbTree t = (SmbTree)e.nextElement();
/*
* Logoff And X Request / Response
*/
-
+
SmbComLogoffAndX request = new SmbComLogoffAndX( null );
request.uid = uid;
try {
uid = 0;
}
- sessionSetup = false;
+ connectionState = 0;
+ transport.notifyAll();
}
}
public String toString() {
return "SmbSession[accountName=" + auth.username +
",primaryDomain=" + auth.domain +
",uid=" + uid +
- ",sessionSetup=" + sessionSetup + "]";
+ ",connectionState=" + connectionState + "]";
}
}
int port, mid;
OutputStream out;
InputStream in;
- byte[] sbuf = new byte[255]; /* small local buffer */
+ byte[] sbuf = new byte[512]; /* small local buffer */
SmbComBlankResponse key = new SmbComBlankResponse();
long sessionExpiration = System.currentTimeMillis() + SO_TIMEOUT;
LinkedList referrals = new LinkedList();
/* WordCount thru dataOffset always 27 */
readn( in, BUF, 4 + off, 27 ); off += 27;
resp.decode( BUF, 4 );
- if (r.dataLength > 0) {
- readn( in, BUF, 4 + off, r.dataOffset - off); /* pad */
+ /* EMC can send pad w/o data */
+ int pad = r.dataOffset - off;
+ if (r.byteCount > 0 && pad > 0 && pad < 4)
+ readn( in, BUF, 4 + off, pad);
+
+ if (r.dataLength > 0)
readn( in, r.b, r.off, r.dataLength ); /* read direct */
- }
} else {
readn( in, BUF, 4 + 32, size - 32 );
resp.decode( BUF, 4 );
makeKey( req );
}
- synchronized (response_map) {
+ synchronized (this) {
response.received = false;
resp.isReceived = false;
try {
long timeout = RESPONSE_TIMEOUT;
resp.expiration = System.currentTimeMillis() + timeout;
while( resp.hasMoreElements() ) {
- response_map.wait( timeout );
+ wait( timeout );
timeout = resp.expiration - System.currentTimeMillis();
if (timeout <= 0) {
throw new TransportException( this +
}
} catch( SmbException se ) {
throw se;
- } catch( InterruptedException ie ) {
- throw new SmbException( ie.getMessage(), ie );
} catch( IOException ioe ) {
throw new SmbException( ioe.getMessage(), ioe );
}
private static int tree_conn_counter;
- private int tid;
+ /* 0 - not connected
+ * 1 - connecting
+ * 2 - connected
+ * 3 - disconnecting
+ */
+ int connectionState;
+ int tid;
String share;
String service = "?????";
String service0;
SmbSession session;
- boolean treeConnected, inDfs, inDomainDfs;
- int tree_num;
+ boolean inDfs, inDomainDfs;
+ int tree_num; // used by SmbFile.isOpen
SmbTree( SmbSession session, String share, String service ) {
this.session = session;
this.service = service;
}
this.service0 = this.service;
+ this.connectionState = 0;
}
boolean matches( String share, String service ) {
}
void send( ServerMessageBlock request,
ServerMessageBlock response ) throws SmbException {
+synchronized (session.transport()) {
if( response != null ) {
response.received = false;
}
}
throw se;
}
+}
}
void treeConnect( ServerMessageBlock andx,
ServerMessageBlock andxResponse ) throws SmbException {
- String unc;
- SmbTransport transport = session.transport();
-synchronized(transport.setupDiscoLock) {
-synchronized(transport) {
+synchronized (session.transport()) {
+ String unc;
- if (treeConnected) {
- return;
+ while (connectionState != 0) {
+ if (connectionState == 2 || connectionState == 3) // connected or disconnecting
+ return;
+ try {
+ session.transport.wait();
+ } catch (InterruptedException ie) {
+ throw new SmbException(ie.getMessage(), ie);
+ }
}
+ connectionState = 1; // trying ...
- /* The hostname to use in the path is only known for
- * sure if the NetBIOS session has been successfully
- * established.
- */
-
- session.transport.connect();
-
- unc = "\\\\" + session.transport.tconHostName + '\\' + share;
-
- /* IBM iSeries doesn't like specifying a service. Always reset
- * the service to whatever was determined in the constructor.
- */
- service = service0;
-
- /*
- * Tree Connect And X Request / Response
- */
-
- if( session.transport.log.level >= 4 )
- session.transport.log.println( "treeConnect: unc=" + unc + ",service=" + service );
-
- SmbComTreeConnectAndXResponse response =
- new SmbComTreeConnectAndXResponse( andxResponse );
- SmbComTreeConnectAndX request =
- new SmbComTreeConnectAndX( session, unc, service, andx );
- session.send( request, response );
-
- tid = response.tid;
- service = response.service;
- inDfs = response.shareIsInDfs;
- treeConnected = true;
- tree_num = tree_conn_counter++;
-}
+ try {
+ /* The hostname to use in the path is only known for
+ * sure if the NetBIOS session has been successfully
+ * established.
+ */
+
+ session.transport.connect();
+
+ unc = "\\\\" + session.transport.tconHostName + '\\' + share;
+
+ /* IBM iSeries doesn't like specifying a service. Always reset
+ * the service to whatever was determined in the constructor.
+ */
+ service = service0;
+
+ /*
+ * Tree Connect And X Request / Response
+ */
+
+ if( session.transport.log.level >= 4 )
+ session.transport.log.println( "treeConnect: unc=" + unc + ",service=" + service );
+
+ SmbComTreeConnectAndXResponse response =
+ new SmbComTreeConnectAndXResponse( andxResponse );
+ SmbComTreeConnectAndX request =
+ new SmbComTreeConnectAndX( session, unc, service, andx );
+ session.send( request, response );
+
+ tid = response.tid;
+ service = response.service;
+ inDfs = response.shareIsInDfs;
+ tree_num = tree_conn_counter++;
+
+ connectionState = 2; // connected
+ } catch (SmbException se) {
+ treeDisconnect(true);
+ connectionState = 0;
+ throw se;
+ }
}
}
void treeDisconnect( boolean inError ) {
-synchronized( session.transport ) {
- if (treeConnected && !inError && tid != 0) {
+synchronized (session.transport()) {
+
+ if (connectionState != 2) // not-connected
+ return;
+ connectionState = 3; // disconnecting
+
+ if (!inError && tid != 0) {
try {
send( new SmbComTreeDisconnect(), null );
} catch( SmbException se ) {
}
}
}
- treeConnected = false;
inDfs = false;
inDomainDfs = false;
+
+ connectionState = 0;
+
+ session.transport.notifyAll();
}
}
",tid=" + tid +
",inDfs=" + inDfs +
",inDomainDfs=" + inDomainDfs +
- ",treeConnected=" + treeConnected + "]";
+ ",connectionState=" + connectionState + "]";
}
}
--- /dev/null
+package jcifs.smb;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+public class TestLocking implements Runnable
+{
+
+ int numThreads = 1;
+ int numIter = 1;
+ long delay = 100;
+ String url = null;
+ int numComplete = 0;
+ long ltime = 0L;
+
+ public void run()
+ {
+ try {
+ SmbFile f = new SmbFile(url);
+ SmbFile d = new SmbFile(f.getParent());
+ byte[] buf = new byte[1024];
+
+ for (int ii = 0; ii < numIter; ii++) {
+
+ synchronized (this) {
+ ltime = System.currentTimeMillis();
+ wait();
+ }
+
+ try {
+ double r = Math.random();
+ if (r < 0.333) {
+ f.exists();
+// System.out.print('e');
+ } else if (r < 0.667) {
+ d.listFiles();
+// System.out.print('l');
+ } else if (r < 1.0) {
+ InputStream in = f.getInputStream();
+ while (in.read(buf) > 0) {
+// System.out.print('r');
+ }
+ in.close();
+ }
+ } catch (IOException ioe) {
+ System.err.println(ioe.getMessage());
+//ioe.printStackTrace(System.err);
+ }
+
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ numComplete++;
+ }
+ }
+
+ public static void main(String[] args) throws Exception
+ {
+ if (args.length < 1) {
+ System.err.println("usage: TestLocking [-t <numThreads>] [-i <numIter>] [-d <delay>] url");
+ System.exit(1);
+ }
+
+ TestLocking t = new TestLocking();
+ t.ltime = System.currentTimeMillis();
+
+ for (int ai = 0; ai < args.length; ai++) {
+ if (args[ai].equals("-t")) {
+ ai++;
+ t.numThreads = Integer.parseInt(args[ai]);
+ } else if (args[ai].equals("-i")) {
+ ai++;
+ t.numIter = Integer.parseInt(args[ai]);
+ } else if (args[ai].equals("-d")) {
+ ai++;
+ t.delay = Long.parseLong(args[ai]);
+ } else {
+ t.url = args[ai];
+ }
+ }
+
+ Thread[] threads = new Thread[t.numThreads];
+ int ti;
+
+ for (ti = 0; ti < t.numThreads; ti++) {
+ threads[ti] = new Thread(t);
+ System.out.print(threads[ti].getName());
+ threads[ti].start();
+ }
+
+ while (t.numComplete < t.numThreads) {
+ long delay;
+
+ do {
+ delay = 2L;
+
+ synchronized (t) {
+ long expire = t.ltime + t.delay;
+ long ctime = System.currentTimeMillis();
+
+ if (expire > ctime)
+ delay = expire - ctime;
+ }
+
+if (delay > 2)
+System.out.println("delay=" + delay);
+ Thread.sleep(delay);
+ } while (delay > 2);
+
+ synchronized (t) {
+ t.notifyAll();
+ }
+//System.out.println("numComplete=" + t.numComplete + ",numThreads=" + t.numThreads);
+ }
+
+ for (ti = 0; ti < t.numThreads; ti++) {
+ threads[ti].join();
+ System.out.print(threads[ti].getName());
+ }
+
+ System.out.println();
+ }
+}
protected abstract void doRecv( Response response ) throws IOException;
protected abstract void doSkip() throws IOException;
- public Object setupDiscoLock = new Object();
-
- public void sendrecv( Request request,
+ public synchronized void sendrecv( Request request,
Response response,
long timeout ) throws IOException {
- synchronized (response_map) {
makeKey( request );
response.isReceived = false;
try {
doSend( request );
response.expiration = System.currentTimeMillis() + timeout;
while (!response.isReceived) {
- response_map.wait( timeout );
+ wait( timeout );
timeout = response.expiration - System.currentTimeMillis();
if (timeout <= 0) {
throw new TransportException( name +
} finally {
response_map.remove( request );
}
- }
}
private void loop() {
while( thread == Thread.currentThread() ) {
Request key = peekKey();
if (key == null)
throw new IOException( "end of stream" );
- synchronized (response_map) {
+ synchronized (this) {
Response response = (Response)response_map.get( key );
if (response == null) {
if (log.level >= 4)
} else {
doRecv( response );
response.isReceived = true;
- response_map.notifyAll();
+ notifyAll();
}
}
} catch( Exception ex ) {
}
}
}
- public void disconnect( boolean hard ) throws IOException {
- synchronized(setupDiscoLock) {
- synchronized(this) {
- switch (state) {
- case 0: /* not connected - just return */
- return;
- case 2:
- hard = true;
- case 3: /* connected - go ahead and disconnect */
- if (response_map.size() != 0 && !hard) {
- break; /* outstanding requests */
- }
- doDisconnect( hard );
- case 4: /* in error - reset the transport */
- thread = null;
- state = 0;
- break;
- default:
- if (log.level >= 1)
- log.println("Invalid state: " + state);
- thread = null;
- state = 0;
- break;
+ public synchronized void disconnect( boolean hard ) throws IOException {
+ switch (state) {
+ case 0: /* not connected - just return */
+ return;
+ case 2:
+ hard = true;
+ case 3: /* connected - go ahead and disconnect */
+ if (response_map.size() != 0 && !hard) {
+ break; /* outstanding requests */
}
- }
+ doDisconnect( hard );
+ case 4: /* in error - reset the transport */
+ thread = null;
+ state = 0;
+ break;
+ default:
+ if (log.level >= 1)
+ log.println("Invalid state: " + state);
+ thread = null;
+ state = 0;
+ break;
}
}
public void run() {