From 1626b57f17eb4f213774dbedfd17b4fd600c0fa4 Mon Sep 17 00:00:00 2001 From: Felix Schumacher Date: Wed, 6 Aug 2008 16:17:41 +0200 Subject: [PATCH] jcifs-0.9.2 from tgz Removed the HTTP redirect from the filter. Tue Jun 1 23:23:08 EDT 2004 jcifs-0.9.1 released If the "guest" account is enabled on a CIFS server it is possible for jCIFS to successfully authenticate even with an invalid username. Windows networks virtually always disable this account but apparently it is not difficult to enable this condition when using Samba. NTLM HTTP Filter users that are not certain the "guest" account is disable should upgrade. This release eliminates the possability of authenticating anyone without a valid username. Specifically, a small clause as been added that throws an SmbAuthException if the server responds with the "is logged in as guest" bit on. Thu May 27 23:25:57 EDT 2004 jcifs-0.9.0 released The 0.9 series is no longer beta. Aside from a very small code change this release only includes documentation updates. --- CHANGES.txt | 123 +++++- README.txt | 27 ++ build.xml | 53 ++- examples/ntlm.prp | 37 -- examples/runtests.sh | 2 +- examples/web.xml | 59 +++ src/jcifs/Config.java | 6 + src/jcifs/UniAddress.java | 85 +++- src/jcifs/http/NetworkExplorer.java | 34 +- src/jcifs/http/NtlmHttpFilter.java | 18 +- src/jcifs/http/NtlmSsp.java | 2 +- src/jcifs/netbios/Name.java | 5 +- src/jcifs/netbios/NameServiceClient.java | 60 ++- src/jcifs/netbios/NbtAddress.java | 11 +- src/jcifs/ntlmssp/NtlmMessage.java | 5 +- src/jcifs/smb/AndXServerMessageBlock.java | 10 + src/jcifs/smb/NetServerEnum2.java | 27 +- src/jcifs/smb/NetServerEnum2Response.java | 9 +- src/jcifs/smb/NtStatus.java | 3 + src/jcifs/smb/NtlmPasswordAuthentication.java | 12 +- src/jcifs/smb/ServerMessageBlock.java | 12 +- src/jcifs/smb/SigningDigest.java | 1 - src/jcifs/smb/SmbComSessionSetupAndX.java | 25 +- src/jcifs/smb/SmbComSessionSetupAndXResponse.java | 3 +- src/jcifs/smb/SmbComTransaction.java | 1 + src/jcifs/smb/SmbFile.java | 506 ++++++++++++++++------ src/jcifs/smb/SmbSession.java | 38 +- src/jcifs/smb/SmbTransport.java | 44 +- src/jcifs/smb/SmbTree.java | 3 +- 29 files changed, 898 insertions(+), 323 deletions(-) delete mode 100644 examples/ntlm.prp create mode 100644 examples/web.xml diff --git a/CHANGES.txt b/CHANGES.txt index eddd740..378efb4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,19 +1,118 @@ +Thu May 27 23:25:57 EDT 2004 +jcifs-0.9.0 released + +The 0.9 series is no longer beta as there have been no reports of problems. +The documentation has been updated but still needs a little work. Please +report anything inaccurate or confusing. + +Thu May 13 23:34:43 EDT 2004 +jcifs-0.9.0p released + +Thus begins the 0.9 series. See multiple entries below for details. + +Thu May 13 18:52:52 EDT 2004 + +NetServerEnum2 has been added to support large lists of workgroups and +servers. Note that enumerating large lists of shares is still limited by +one 64K buffer. + +The NtlmHttpFilter has been modified to support "preauthentication" using +the default credentials so that a valid signing key can be generated for +servers that require SMB signing (although it will probably still work +without). + +Mon May 10 21:56:49 EDT 2004 + +Several minor changes have been made to the NTLM HTTP Authentication +Filter. First, it is now not possible for an NtlmPasswordAuthentication +object (NPA) with externally supplied hashes (i.e. via NTLM HTTP Filter) to +be used with an SMB_COM_SESSION_SETUP if the challenge used to build the +hashes no longer matches the transport over which the request is being +issued (i.e. the challenge has expired). Second, should such a condition +arise an SmbAuthException will be thrown from SmbSession.sessionSetup with +a status of NT_STATUS_ACCESS_VIOLATION (this status code is not returned by +the server in this case but is not used by any other natrually occuring +SmbAuthException but could in theory be emitted as a plain SmbException). +The NtlmHttpFilter will catch this exception and issue an HTTP redirect to +the requested URL thereby reinitiating the NTLM negotiation. In theory this +condition cannot arise as the NPA's presence in the HttpSession is all that +is necessary for the request to bypass NTLM and call chain.doFilter but it +is believed there are unforseen conditions under which the Filter can +provoke NTLM to be renegotiated back-to-back thereby causing the Network +Password dialog to appear (clustering env?) so this modification should +ensures bad password hashes are never passed to the domain controller and +any error will be transparent to the user due to the redirect (i.e. the +Network Password dialog will not be provoked). + +The NetworkExplorer servlet has been adjusted to accomodate underlying +changes that manafested themselves as excessive netbios name querying for +workgoup and server names. The NT_STATUS_ACCESS_VIOLATION challenge check +has also been implemented in NetworkExplorer. These changes permit +NetworkExplorer to browse across DFS shares. When a directory is discovered +to be in DFS, a DfsReferral is thrown. This exception is caught by +NetworkExplorer which then issues a redirect with a new URL build with the +DfsReferral.node string. + Sun May 2 22:57:30 EDT 2004 -Started to add additional WINS entry support but still only the first entry is used. +Multiple WINS servers may now be specified as a comma separated list. + +Most SMB signature code has been moved into jcifs/smb/SigningDigest.java +where it's state can more easily be tracked. This is more favorable for +dealing with issues such as failure during signature initialization or the +scenario where NULL creds to IPC$ should not use signing after +LOGON_FAILURE with previous session setup. + +The behavior of SmbNamedPipe.getOutputStream has changed. If +PIPE_TYPE_TRANSACT and PIPE_TYPE_CALL are NOT specified a +TRANS_WAIT_NAMED_PIPE will be issued before the file is opened. This +emulates a WaitNamedPipe follow by CreateFile client. + +The return value of readWriteFormat is now checked against the NBT header +length (from in.available()). If it does not match it is skip()'d and a log +message is written. This should allow jCIFS to get by the OS/400 bug in the +Negotiate response. + +The ssnLimit property will now cause new transports to be created to +accomodate new sessions as opposed to the previous behavior of closing +stale sessions when the ssnLimit is reached. Values can be 0 meaning +unlimited, 1 meaning strict 1:1 sessions:transports, and N for N sessions +per transport. A value of 1 should permit the NTLM HTTP Filter to operated +with domain controllers that require SMB signatures. This functionality +should prove to be useful for a variety of other reasons as well. + +Added additional checks to close NbtSocket in connect() in the event of an +error. This should curb the CLOSE_WAIT sockets reported by Gary but +apparently this is not enough to completely eliminate them as traces show +even an ACK'd FIN can still leave a socket in this state. + +The SmbFiles resulting from listing a workgroup could result in those files +have type TYPE_WORKGROUP when they should have been TYPE_SERVER. + +Several bugs in the SMB signatures code have been fixed; 1) a concurrency +flaw could result in the signing digest being updated inappropriately 2) +the signature of an errant session setup should be ignored 3) a padding +byte was not properly considered that could sporatically result in +"Unverifiable signature" errors reading files (this is the outstanding +mystery bug mentioned in the 0.7.19 release). -Most SMB signature code has been moved into jcifs/smb/SigningDigest.java where it's state can more easily be tracked. This is more favorable for dealing with issues such as failure during signature initialization or the scenario where NULL creds to IPC$ should not use signing after LOGON_FAILURE with previous session setup. -The behavior of SmbNamedPipe.getOutputStream has changed. If PIPE_TYPE_TRANSACT and PIPE_TYPE_CALL are NOT specified a TRANS_WAIT_NAMED_PIPE will be issued before the file is opened. This emulates a WaitNamedPipe follow by CreateFile client. -The return value of readWriteFormat is now checked against the NBT header length (from in.available()). If it does not match it is skip()'d and a log message is written. This should allow jCIFS to get by the OS/400 bug in the Negotiate response. -The ssnLimit property will now cause new transports to be created to accomodate new sessions as opposed to the previous behavior of closing stale sessions when the ssnLimit is reached. Values can be 0 meaning unlimited, 1 meaning strict 1:1 sessions:transports, and N for N sessions per transport. A value of 1 should permit the NTLM HTTP Filter to operated with domain controllers that require SMB signatures. This functionality should prove to be useful for a variety of other reasons as well. -Added additional checks to close NbtSocket in connect() in the event of an error. This should curb the CLOSE_WAIT sockets reported by Gary but apparently this is not enough to completely eliminate them as traces show even an ACK'd FIN can still leave a socket in this state. -The SmbFiles resulting from listing a workgroup could result in those files have type TYPE_WORKGROUP when they should have been TYPE_SERVER. -Several bugs in the SMB signatures code have been fixed; 1) a concurrency flaw could result in the signing digest being updated inappropriately 2) the signature of an errant session setup should be ignored 3) a padding byte was not properly considered that could sporatically result in "Unverifiable signature" errors reading files (this is the outstanding mystery bug mentioned in the 0.7.19 release). Fixed some incorrect path handling in the DFS referral code. -Two property defaults have been changed; jcifs.smb.client.listSize from 1200 to 65535 and jcifs.smb.client.listCount from 15 to 200. For high latency networks I recommend changing the values back. -All access specifiers have been reviewed and changed to private or protected wherever possible and appropriate. -The NetBIOS socket OutputStream has been reduced to a virtually useless shell. The NetBIOS header is now handled within ServerMessageBlock.java. Unfortunately it does not appear to have improved performance considerably. It's hard to tell from the testing performed. -The MpxControl inner class has been removed in favor of something much simpler. It no longer creates Integer objects with each transmission. Unfortunately it does not appear to have improved performance either. + +Two property defaults have been changed; jcifs.smb.client.listSize from +1200 to 65535 and jcifs.smb.client.listCount from 15 to 200. For high +latency networks I recommend changing the values back. + +All access specifiers have been reviewed and changed to private or +protected wherever possible and appropriate. + +The NetBIOS socket OutputStream has been reduced to a virtually useless +shell. The NetBIOS header is now handled within ServerMessageBlock.java. +Unfortunately it does not appear to have improved performance considerably. +It's hard to tell from the testing performed. + +The MpxControl inner class has been removed in favor of something much +simpler. It no longer creates Integer objects with each transmission. +Unfortunately it does not appear to have improved performance either. Tue Feb 10 23:44:45 EST 2004 jcifs-0.8.0 released diff --git a/README.txt b/README.txt index fd30bf5..5de5b2b 100644 --- a/README.txt +++ b/README.txt @@ -1,3 +1,30 @@ +Removed the HTTP redirect from the filter. + +Tue Jun 1 23:23:08 EDT 2004 +jcifs-0.9.1 released + +If the "guest" account is enabled on a CIFS server it is possible for jCIFS +to successfully authenticate even with an invalid username. Windows +networks virtually always disable this account but apparently it is not +difficult to enable this condition when using Samba. NTLM HTTP Filter users +that are not certain the "guest" account is disable should upgrade. This +release eliminates the possability of authenticating anyone without a valid +username. Specifically, a small clause as been added that throws an +SmbAuthException if the server responds with the "is logged in as guest" +bit on. + +Thu May 27 23:25:57 EDT 2004 +jcifs-0.9.0 released + +The 0.9 series is no longer beta. Aside from a very small code change this +release only includes documentation updates. + +Thu May 13 23:34:43 EDT 2004 +jcifs-0.9.0p released + +Thus begins the 0.9 series. See CHANGES.txt for details. The documentation +hasn't been revisited so some of it is incorrect. + Tue Feb 10 23:44:45 EST 2004 jcifs-0.8.0 released diff --git a/build.xml b/build.xml index babab71..0d3d126 100644 --- a/build.xml +++ b/build.xml @@ -1,6 +1,6 @@ - + @@ -64,8 +64,9 @@ dependencies: Checks that all class dependencies are met. author="true" version="true" use="true" - sourcefiles="src/jcifs/Config.java,src/jcifs/UniAddress.java,src/jcifs/http/NetworkExplorer.java,src/jcifs/http/NtlmHttpFilter.java,src/jcifs/http/NtlmServlet.java,src/jcifs/http/NtlmSsp.java,src/jcifs/netbios/NbtAddress.java,src/jcifs/smb/DosFileFilter.java,src/jcifs/smb/NtlmAuthenticator.java,src/jcifs/smb/NtlmPasswordAuthentication.java,src/jcifs/smb/SmbAuthException.java,src/jcifs/smb/SmbException.java,src/jcifs/smb/SmbFile.java,src/jcifs/smb/SmbFileFilter.java,src/jcifs/smb/SmbFileInputStream.java,src/jcifs/smb/SmbFilenameFilter.java,src/jcifs/smb/SmbFileOutputStream.java,src/jcifs/smb/SmbNamedPipe.java,src/jcifs/smb/SmbRandomAccessFile.java,src/jcifs/smb/SmbSession.java" - windowtitle="jCIFS API"> + sourcefiles="src/jcifs/Config.java,src/jcifs/UniAddress.java,src/jcifs/netbios/NbtAddress.java,src/jcifs/smb/DosFileFilter.java,src/jcifs/smb/NtlmAuthenticator.java,src/jcifs/smb/NtlmPasswordAuthentication.java,src/jcifs/smb/SmbAuthException.java,src/jcifs/smb/SmbException.java,src/jcifs/smb/SmbFile.java,src/jcifs/smb/SmbFileFilter.java,src/jcifs/smb/SmbFileInputStream.java,src/jcifs/smb/SmbFilenameFilter.java,src/jcifs/smb/SmbFileOutputStream.java,src/jcifs/smb/SmbNamedPipe.java,src/jcifs/smb/SmbRandomAccessFile.java,src/jcifs/smb/SmbSession.java" + windowtitle="JCIFS API"> + @@ -74,7 +75,7 @@ dependencies: Checks that all class dependencies are met. - + @@ -94,6 +95,19 @@ dependencies: Checks that all class dependencies are met. + + + + + + + + + + + + + @@ -164,4 +178,35 @@ dependencies: Checks that all class dependencies are met. + + + + + diff --git a/examples/ntlm.prp b/examples/ntlm.prp deleted file mode 100644 index 5087008..0000000 --- a/examples/ntlm.prp +++ /dev/null @@ -1,37 +0,0 @@ -! Lookup any domain contorller in the FOONET<1C> domain - -;jcifs.smb.client.domain=foonet - -! Or explicitly specify any SMB server to use as a -! "Domain Controller". This could be a real PDC or -! preferrably a BDC or just a plain workstation - -jcifs.smb.client.domainController=192.168.1.15 - -! WINS server is only necessary if you are using the -! jcifs.smb.client.domain property to lookup a domain -! controller - -;jcifs.netbios.wins=136.135.65.85 - -! On machines with multiple interfaces it is usually -! necessary to set the broadcast address. See the jCIFS FAQ. - -jcifs.netbios.baddr=192.168.1.255 - -! If we're sure we have a WINS server this is a -! little more direct - -;jcifs.resolveOrder=WINS - -! Users will have to re-authenticate after 600000ms -! (10 minutes) if no clients trigger activity on the socket -! to the domain controller - -jcifs.smb.client.soTimeout=600000 - -! Uncomment this if it's not working to see what's going on. -! Logging output is directed to System.err. On Tomcat this -! goes to logs/catalina.out. Not sure about Resin. - -;jcifs.util.loglevel=10 diff --git a/examples/runtests.sh b/examples/runtests.sh index 0940d65..6978e66 100644 --- a/examples/runtests.sh +++ b/examples/runtests.sh @@ -5,7 +5,7 @@ CLASSPATH=../build:. PROPERTIES=../../miallen.prp RUN="${JAVA_HOME}/bin/java -cp ${CLASSPATH} -Djcifs.properties=${PROPERTIES}" -SERVER=miallen2 +SERVER=rnycwbmallen1 SHARE=pub WRITE_DIR=test SRC_DIR=test/variety diff --git a/examples/web.xml b/examples/web.xml new file mode 100644 index 0000000..ec3c7f1 --- /dev/null +++ b/examples/web.xml @@ -0,0 +1,59 @@ + + + + + + jcifs.http.domainController + 172.23.22.27 + + + jcifs.smb.client.soTimeout + 8000 + + + jcifs.smb.client.domain + AMRS + + + jcifs.smb.client.username + produser1 + + + jcifs.smb.client.password + control96 + + + jcifs.smb.client.signingPreferred + true + + + + + + + diff --git a/src/jcifs/Config.java b/src/jcifs/Config.java index e3faadb..6a4419e 100644 --- a/src/jcifs/Config.java +++ b/src/jcifs/Config.java @@ -276,6 +276,12 @@ public class Config { return def; } + /** + * Retrieve an array of InetAddress created from a property + * value containting a delim separated list of hostnames and/or + * ipaddresses. + */ + public static InetAddress[] getInetAddressArray( String key, String delim, InetAddress[] def ) { String p = getProperty( key ); if( p != null ) { diff --git a/src/jcifs/UniAddress.java b/src/jcifs/UniAddress.java index ef27421..504334b 100644 --- a/src/jcifs/UniAddress.java +++ b/src/jcifs/UniAddress.java @@ -40,8 +40,9 @@ import jcifs.util.LogStream; * jCIFS name resolution properties can greatly affect the behavior of * the client and may be necessary for proper operation. *

- * This class should be used in favor of InetAddress to resolve hostnames on LANs and WANs that - * support a mixture of NetBIOS/WINS and DNS resolvable hosts. + * This class should be used in favor of InetAddress to resolve + * hostnames on LANs and WANs that support a mixture of NetBIOS/WINS and + * DNS resolvable hosts. */ public class UniAddress { @@ -58,7 +59,7 @@ public class UniAddress { static { String ro = Config.getProperty( "jcifs.resolveOrder" ); - InetAddress nbns = NbtAddress.getNBNSAddress(); + InetAddress nbns = NbtAddress.getWINSAddress(); try { baddr = Config.getInetAddress( "jcifs.netbios.baddr", @@ -160,7 +161,7 @@ public class UniAddress { static NbtAddress lookupServerOrWorkgroup( String name, InetAddress svr ) throws UnknownHostException { Sem sem = new Sem( 2 ); - int type = NbtAddress.isNBNS( svr ) ? 0x1b : 0x1d; + int type = NbtAddress.isWINS( svr ) ? 0x1b : 0x1d; QueryThread q1x = new QueryThread( sem, name, type, null, svr ); QueryThread q20 = new QueryThread( sem, name, 0x20, null, svr ); @@ -187,13 +188,13 @@ public class UniAddress { } } -/** - * Determines the address of a host given it's host name. The name can be a - * machine name like "jcifs.samba.org", or an IP address like "192.168.1.15". - * - * @param host NetBIOS or DNS hostname to resolve - * @throws java.net.UnknownHostException if there is an error resolving the name - */ + /** + * Determines the address of a host given it's host name. The name can be a + * machine name like "jcifs.samba.org", or an IP address like "192.168.1.15". + * + * @param host NetBIOS or DNS hostname to resolve + * @throws java.net.UnknownHostException if there is an error resolving the name + */ public static UniAddress getByName( String hostname ) throws UnknownHostException { @@ -228,6 +229,12 @@ public class UniAddress { return Character.isDigit( hostname.charAt( 0 )) == false; } + /** + * Lookup hostname and return it's UniAddress. If the + * possibleNTDomainOrWorkgroup parameter is true an + * addtional name query will be performed to locate a master browser. + */ + public static UniAddress getByName( String hostname, boolean possibleNTDomainOrWorkgroup ) throws UnknownHostException { @@ -257,9 +264,9 @@ public class UniAddress { continue; } if( possibleNTDomainOrWorkgroup ) { - addr = lookupServerOrWorkgroup( hostname, NbtAddress.getNBNSAddress() ); + addr = lookupServerOrWorkgroup( hostname, NbtAddress.getWINSAddress() ); } else { - addr = NbtAddress.getByName( hostname, 0x20, null, NbtAddress.getNBNSAddress() ); + addr = NbtAddress.getByName( hostname, 0x20, null, NbtAddress.getWINSAddress() ); } break; case RESOLVER_BCAST: @@ -294,8 +301,10 @@ public class UniAddress { String calledName; /** - * Wrap an InetAddress or NbtAddress. + * Create a UniAddress by wrapping an InetAddress or + * NbtAddress. */ + public UniAddress( Object addr ) { if( addr == null ) { throw new IllegalArgumentException(); @@ -303,17 +312,28 @@ public class UniAddress { this.addr = addr; } + /** + * Return the IP address of this address as a 32 bit integer. + */ + public int hashCode() { return addr.hashCode(); } + + /** + * Compare two addresses for equality. Two UniAddresss are equal + * if they are both UniAddress' and refer to the same IP address. + */ + public boolean equals( Object obj ) { return obj instanceof UniAddress && addr.hashCode() == obj.hashCode(); -// addr.equals( ((UniAddress)obj).addr ); } -/** Guess first called name to try for session establishment. This - * methods are used by the smb package. - */ + /** + * Guess first called name to try for session establishment. This + * method is used exclusively by the jcifs.smb package. + */ + public String firstCalledName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).firstCalledName(); @@ -335,9 +355,12 @@ public class UniAddress { return calledName; } -/** Guess next called name to try for session establishment. This - * methods are used by the smb package. - */ + + /** + * Guess next called name to try for session establishment. This + * method is used exclusively by the jcifs.smb package. + */ + public String nextCalledName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).nextCalledName(); @@ -347,21 +370,41 @@ public class UniAddress { } return null; } + + /** + * Return the underlying NbtAddress or InetAddress. + */ + public Object getAddress() { return addr; } + + /** + * Return the hostname of this address such as "MYCOMPUTER". + */ + public String getHostName() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).getHostName(); } return ((InetAddress)addr).getHostName(); } + + /** + * Return the IP address as text such as "192.168.1.15". + */ + public String getHostAddress() { if( addr instanceof NbtAddress ) { return ((NbtAddress)addr).getHostAddress(); } return ((InetAddress)addr).getHostAddress(); } + + /** + * Return the a text representation of this address such as + * MYCOMPUTER/192.168.1.15. + */ public String toString() { return addr.toString(); } diff --git a/src/jcifs/http/NetworkExplorer.java b/src/jcifs/http/NetworkExplorer.java index c6af505..279a66a 100644 --- a/src/jcifs/http/NetworkExplorer.java +++ b/src/jcifs/http/NetworkExplorer.java @@ -57,8 +57,8 @@ public class NetworkExplorer extends HttpServlet { int n; String name; - Config.setProperty( "jcifs.smb.client.soTimeout", "300000" ); - Config.setProperty( "jcifs.smb.client.attrExpirationPeriod", "120000" ); + Config.setProperty( "jcifs.smb.client.soTimeout", "600000" ); + Config.setProperty( "jcifs.smb.client.attrExpirationPeriod", "300000" ); Enumeration e = getInitParameterNames(); while( e.hasMoreElements() ) { @@ -206,6 +206,10 @@ public class NetworkExplorer extends HttpServlet { continue; } } catch( SmbAuthException sae ) { + } catch( SmbException se ) { + if( se.getNtStatus() != se.NT_STATUS_UNSUCCESSFUL ) { + throw se; + } } if( dirents[i].isDirectory() ) { dirCount++; @@ -372,9 +376,12 @@ public class NetworkExplorer extends HttpServlet { if( p == len ) { return null; } - do { /* collect server name */ - out[i++] = (ch = pathInfo.charAt( p++ )); - } while( p < len && ch != '/' ); + + /* collect server name */ + while ( p < len && ( ch = pathInfo.charAt( p )) != '/' ) { + out[i++] = ch; + p++; + } while( p < len && pathInfo.charAt( p ) == '/' ) { p++; } @@ -406,7 +413,8 @@ public class NetworkExplorer extends HttpServlet { msg = req.getHeader( "Authorization" ); offerBasic = enableBasic && (insecureBasic || req.isSecure()); - if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) { + if( msg != null && (msg.startsWith( "NTLM " ) || + (offerBasic && msg.startsWith("Basic ")))) { if( msg.startsWith("NTLM ")) { byte[] challenge; @@ -418,12 +426,12 @@ public class NetworkExplorer extends HttpServlet { dc = UniAddress.getByName( server, possibleWorkgroup ); } - req.getSession(); + req.getSession(); /* ensure session id is set for cluster env. */ challenge = SmbSession.getChallenge( dc ); if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) { return; } - } else { + } else { /* Basic */ String auth = new String( Base64.decode( msg.substring(6) ), "US-ASCII" ); int index = auth.indexOf( ':' ); String user = (index != -1) ? auth.substring(0, index) : auth; @@ -473,6 +481,13 @@ public class NetworkExplorer extends HttpServlet { if( ssn != null ) { ssn.removeAttribute( "npa-" + server ); } + if( sae.getNtStatus() == sae.NT_STATUS_ACCESS_VIOLATION ) { + /* Server challenge no longer valid for + * externally supplied password hashes. + */ + resp.sendRedirect( req.getRequestURL().toString() ); + return; + } resp.setHeader( "WWW-Authenticate", "NTLM" ); if (offerBasic) { resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + realm + "\""); @@ -484,9 +499,6 @@ public class NetworkExplorer extends HttpServlet { } catch( DfsReferral dr ) { StringBuffer redir = req.getRequestURL(); String qs = req.getQueryString(); -if( true ) { - throw new RuntimeException( "DFS referrals with NetworkExplorer are currently disabled because they can lead to account lockout." ); -} redir = new StringBuffer( redir.substring( 0, redir.length() - req.getPathInfo().length() )); redir.append( dr.node.replace( '\\', '/' )); redir.append( '/' ); diff --git a/src/jcifs/http/NtlmHttpFilter.java b/src/jcifs/http/NtlmHttpFilter.java index fc6b940..730f138 100644 --- a/src/jcifs/http/NtlmHttpFilter.java +++ b/src/jcifs/http/NtlmHttpFilter.java @@ -44,16 +44,12 @@ import jcifs.netbios.NbtAddress; public class NtlmHttpFilter implements Filter { - private String defaultDomain; + private String defaultDomain; private String domainController; - private boolean loadBalance; - private boolean enableBasic; - private boolean insecureBasic; - private String realm; public void init( FilterConfig filterConfig ) throws ServletException { @@ -84,6 +80,7 @@ public class NtlmHttpFilter implements Filter { realm = Config.getProperty("jcifs.http.basicRealm"); if (realm == null) realm = "jCIFS"; } + public void destroy() { } public void doFilter( ServletRequest request, @@ -128,8 +125,19 @@ public class NtlmHttpFilter implements Filter { ntlm = new NtlmPasswordAuthentication(domain, user, password); } try { + SmbSession.logon( dc, ntlm ); + } catch( SmbAuthException sae ) { + if( sae.getNtStatus() == sae.NT_STATUS_ACCESS_VIOLATION ) { + /* Server challenge no longer valid for + * externally supplied password hashes. + */ + HttpSession ssn = req.getSession(false); + if (ssn != null) { + ssn.removeAttribute( "NtlmHttpAuth" ); + } + } resp.setHeader( "WWW-Authenticate", "NTLM" ); if (offerBasic) { resp.addHeader( "WWW-Authenticate", "Basic realm=\"" + diff --git a/src/jcifs/http/NtlmSsp.java b/src/jcifs/http/NtlmSsp.java index 35e0a68..146cf5e 100644 --- a/src/jcifs/http/NtlmSsp.java +++ b/src/jcifs/http/NtlmSsp.java @@ -96,7 +96,7 @@ public class NtlmSsp implements NtlmFlags { byte[] ntResponse = type3.getNTResponse(); if (ntResponse == null) ntResponse = new byte[0]; return new NtlmPasswordAuthentication(type3.getDomain(), - type3.getUser(), lmResponse, ntResponse); + type3.getUser(), challenge, lmResponse, ntResponse); } } else { resp.setHeader("WWW-Authenticate", "NTLM"); diff --git a/src/jcifs/netbios/Name.java b/src/jcifs/netbios/Name.java index acc6716..b06b783 100644 --- a/src/jcifs/netbios/Name.java +++ b/src/jcifs/netbios/Name.java @@ -30,9 +30,8 @@ class Name { private static final String DEFAULT_SCOPE = Config.getProperty( "jcifs.netbios.scope" ); static final String OEM_ENCODING = - Config.getProperty( "jcifs.smb.client.codepage", - Config.getProperty( "jcifs.encoding", - System.getProperty( "file.encoding" ))); + Config.getProperty( "jcifs.encoding", + System.getProperty( "file.encoding" )); String name, scope; int hexCode; diff --git a/src/jcifs/netbios/NameServiceClient.java b/src/jcifs/netbios/NameServiceClient.java index 78f59db..5eef004 100644 --- a/src/jcifs/netbios/NameServiceClient.java +++ b/src/jcifs/netbios/NameServiceClient.java @@ -93,7 +93,7 @@ class NameServiceClient implements Runnable { * been specified. */ - if( NbtAddress.getNBNSAddress() == null ) { + if( NbtAddress.getWINSAddress() == null ) { resolveOrder = new int[2]; resolveOrder[0] = RESOLVER_LMHOSTS; resolveOrder[1] = RESOLVER_BCAST; @@ -112,7 +112,7 @@ class NameServiceClient implements Runnable { if( s.equalsIgnoreCase( "LMHOSTS" )) { tmp[i++] = RESOLVER_LMHOSTS; } else if( s.equalsIgnoreCase( "WINS" )) { - if( NbtAddress.getNBNSAddress() == null ) { + if( NbtAddress.getWINSAddress() == null ) { if( log.level > 1 ) { log.println( "NetBIOS resolveOrder specifies WINS however the " + "jcifs.netbios.wins property has not been set" ); @@ -210,33 +210,49 @@ class NameServiceClient implements Runnable { void send( NameServicePacket request, NameServicePacket response, int timeout ) throws IOException { Integer nid = null; + int count = 0; synchronized( response ) { - try { - synchronized( LOCK ) { - request.nameTrnId = getNextNameTrnId(); - nid = new Integer( request.nameTrnId ); + do { + try { + synchronized( LOCK ) { + request.nameTrnId = getNextNameTrnId(); + nid = new Integer( request.nameTrnId ); - out.setAddress( request.addr ); - out.setLength( request.writeWireFormat( snd_buf, 0 )); - response.received = false; + out.setAddress( request.addr ); + out.setLength( request.writeWireFormat( snd_buf, 0 )); + response.received = false; - responseTable.put( nid, response ); - ensureOpen( timeout + 1000 ); - socket.send( out ); + responseTable.put( nid, response ); + ensureOpen( timeout + 1000 ); + socket.send( out ); - if( log.level > 2 ) { - log.println( request ); - Hexdump.hexdump( log, snd_buf, 0, out.getLength() ); + if( log.level > 2 ) { + log.println( request ); + Hexdump.hexdump( log, snd_buf, 0, out.getLength() ); + } } - } - response.wait( timeout ); + response.wait( timeout ); - } catch( InterruptedException ie ) { - } finally { - responseTable.remove( nid ); - } + } catch( InterruptedException ie ) { + } finally { + responseTable.remove( nid ); + } + + if( !response.received && + NbtAddress.NBNS.length > 1 && + NbtAddress.isWINS( request.addr )) { + /* Message was sent to WINS but + * failed to receive response. + * Try a different WINS server. + */ + request.addr = NbtAddress.switchWINS(); + if( count == 0 ) { + count = NbtAddress.NBNS.length - 1; + } + } + } while( count-- > 0 ); } } @@ -291,7 +307,7 @@ class NameServiceClient implements Runnable { if( resolveOrder[i] == RESOLVER_WINS && name.name != NbtAddress.MASTER_BROWSER_NAME && name.hexCode != 0x1d ) { - request.addr = NbtAddress.getNBNSAddress(); + request.addr = NbtAddress.getWINSAddress(); request.isBroadcast = false; } else { request.addr = baddr; diff --git a/src/jcifs/netbios/NbtAddress.java b/src/jcifs/netbios/NbtAddress.java index ffee207..889a6a4 100644 --- a/src/jcifs/netbios/NbtAddress.java +++ b/src/jcifs/netbios/NbtAddress.java @@ -130,7 +130,7 @@ public final class NbtAddress { public static final int H_NODE = 3; - private static final InetAddress[] NBNS = Config.getInetAddressArray( "jcifs.netbios.wins", ",", new InetAddress[0] ); + static final InetAddress[] NBNS = Config.getInetAddressArray( "jcifs.netbios.wins", ",", new InetAddress[0] ); /* Construct the shared static client object that will * conduct all encoding and decoding of NetBIOS name service @@ -509,11 +509,10 @@ public final class NbtAddress { } } - public static InetAddress getNBNSAddress() { + public static InetAddress getWINSAddress() { return NBNS.length == 0 ? null : NBNS[nbnsIndex]; } - - public static boolean isNBNS( InetAddress svr ) { + public static boolean isWINS( InetAddress svr ) { for( int i = 0; svr != null && i < NBNS.length; i++ ) { if( svr.hashCode() == NBNS[i].hashCode() ) { return true; @@ -521,6 +520,10 @@ public final class NbtAddress { } return false; } + static InetAddress switchWINS() { + nbnsIndex = (nbnsIndex + 1) < NBNS.length ? nbnsIndex + 1 : 0; + return NBNS.length == 0 ? null : NBNS[nbnsIndex]; + } Name hostName; int address, nodeType; diff --git a/src/jcifs/ntlmssp/NtlmMessage.java b/src/jcifs/ntlmssp/NtlmMessage.java index 2a5fd50..c9ed00f 100644 --- a/src/jcifs/ntlmssp/NtlmMessage.java +++ b/src/jcifs/ntlmssp/NtlmMessage.java @@ -35,9 +35,8 @@ public abstract class NtlmMessage implements NtlmFlags { }; private static final String OEM_ENCODING = - Config.getProperty("jcifs.smb.client.codepage", - Config.getProperty("jcifs.encoding", - System.getProperty("file.encoding"))); + Config.getProperty("jcifs.encoding", + System.getProperty("file.encoding")); private int flags; diff --git a/src/jcifs/smb/AndXServerMessageBlock.java b/src/jcifs/smb/AndXServerMessageBlock.java index 925c8ba..b21eb95 100644 --- a/src/jcifs/smb/AndXServerMessageBlock.java +++ b/src/jcifs/smb/AndXServerMessageBlock.java @@ -71,9 +71,15 @@ abstract class AndXServerMessageBlock extends ServerMessageBlock { int writeWireFormat( byte[] dst, int dstIndex ) { int start = headerStart = dstIndex; + dstIndex += writeHeaderWireFormat( dst, dstIndex ); dstIndex += writeAndXWireFormat( dst, dstIndex ); length = dstIndex - start; + + if( digest != null ) { + digest.sign( dst, headerStart, length, this, response ); + } + return length; } @@ -224,6 +230,10 @@ abstract class AndXServerMessageBlock extends ServerMessageBlock { bufferIndex += 2; andxOffset = readInt2( buffer, bufferIndex ); bufferIndex += 2; + + if( andxOffset == 0 ) { /* Snap server workaround */ + andxCommand = (byte)0xFF; + } /* * no point in calling readParameterWordsWireFormat if there are no more diff --git a/src/jcifs/smb/NetServerEnum2.java b/src/jcifs/smb/NetServerEnum2.java index c76838c..8c553ed 100644 --- a/src/jcifs/smb/NetServerEnum2.java +++ b/src/jcifs/smb/NetServerEnum2.java @@ -1,5 +1,6 @@ /* jcifs smb client library in Java * Copyright (C) 2000 "Michael B. Allen" + * Gary Rambo * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,9 +26,12 @@ class NetServerEnum2 extends SmbComTransaction { static final int SV_TYPE_ALL = 0xFFFFFFFF; static final int SV_TYPE_DOMAIN_ENUM = 0x80000000; - static final String DESCR = "WrLehDz\u0000B16BBDz\u0000"; + static final String[] DESCR = { + "WrLehDz\u0000B16BBDz\u0000", + "WrLehDzz\u0000B16BBDz\u0000" + }; - String domain; + String domain, lastName = null; int serverTypes; NetServerEnum2( String domain, int serverTypes ) { @@ -44,20 +48,26 @@ class NetServerEnum2 extends SmbComTransaction { timeout = 5000; } + void reset( int key, String lastName ) { + super.reset(); + this.lastName = lastName; + } + int writeSetupWireFormat( byte[] dst, int dstIndex ) { return 0; } int writeParametersWireFormat( byte[] dst, int dstIndex ) { int start = dstIndex; byte[] descr; + int which = subCommand == NET_SERVER_ENUM2 ? 0 : 1; try { - descr = DESCR.getBytes( "ASCII" ); + descr = DESCR[which].getBytes( "ASCII" ); } catch( UnsupportedEncodingException uee ) { return 0; } - writeInt2( NET_SERVER_ENUM2, dst, dstIndex ); + writeInt2( subCommand & 0xFF, dst, dstIndex ); dstIndex += 2; System.arraycopy( descr, 0, dst, dstIndex, descr.length ); dstIndex += descr.length; @@ -68,6 +78,9 @@ class NetServerEnum2 extends SmbComTransaction { writeInt4( serverTypes, dst, dstIndex ); dstIndex += 4; dstIndex += writeString( domain.toUpperCase(), dst, dstIndex, false ); + if( which == 1 ) { + dstIndex += writeString( lastName.toUpperCase(), dst, dstIndex, false ); + } return dstIndex - start; } @@ -84,6 +97,10 @@ class NetServerEnum2 extends SmbComTransaction { return 0; } public String toString() { - return new String( "NetServerEnum2[" + super.toString() + ",name=" + name + ",serverTypes=" + (serverTypes == SV_TYPE_ALL ? "SV_TYPE_ALL" : "SV_TYPE_DOMAIN_ENUM" ) + "]" ); + return new String( "NetServerEnum2[" + super.toString() + + ",name=" + name + + ",serverTypes=" + (serverTypes == SV_TYPE_ALL ? + "SV_TYPE_ALL" : "SV_TYPE_DOMAIN_ENUM" ) + + "]" ); } } diff --git a/src/jcifs/smb/NetServerEnum2Response.java b/src/jcifs/smb/NetServerEnum2Response.java index dabb0fa..5c89a7b 100644 --- a/src/jcifs/smb/NetServerEnum2Response.java +++ b/src/jcifs/smb/NetServerEnum2Response.java @@ -1,5 +1,6 @@ /* jcifs smb client library in Java * Copyright (C) 2000 "Michael B. Allen" + * Gary Rambo * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,6 +63,8 @@ class NetServerEnum2Response extends SmbComTransactionResponse { private int converter, totalAvailableEntries; + String lastName; + NetServerEnum2Response() { } @@ -93,7 +96,7 @@ class NetServerEnum2Response extends SmbComTransactionResponse { } int readDataWireFormat( byte[] buffer, int bufferIndex, int len ) { int start = bufferIndex; - ServerInfo1 e; + ServerInfo1 e = null; results = new ServerInfo1[numEntries]; for( int i = 0; i < numEntries; i++ ) { @@ -113,6 +116,7 @@ class NetServerEnum2Response extends SmbComTransactionResponse { if( log.level > 2 ) log.println( e ); } + lastName = numEntries == 0 ? null : e.name; return bufferIndex - start; } @@ -122,6 +126,7 @@ class NetServerEnum2Response extends SmbComTransactionResponse { ",status=" + status + ",converter=" + converter + ",entriesReturned=" + numEntries + - ",totalAvailableEntries=" + totalAvailableEntries + "]" ); + ",totalAvailableEntries=" + totalAvailableEntries + + ",lastName=" + lastName + "]" ); } } diff --git a/src/jcifs/smb/NtStatus.java b/src/jcifs/smb/NtStatus.java index 8e0213a..58f9782 100644 --- a/src/jcifs/smb/NtStatus.java +++ b/src/jcifs/smb/NtStatus.java @@ -28,6 +28,7 @@ public interface NtStatus { public static final int NT_STATUS_UNSUCCESSFUL = 0xC0000001; public static final int NT_STATUS_NOT_IMPLEMENTED = 0xC0000002; public static final int NT_STATUS_INVALID_INFO_CLASS = 0xC0000003; + public static final int NT_STATUS_ACCESS_VIOLATION = 0xC0000005; public static final int NT_STATUS_INVALID_HANDLE = 0xC0000008; public static final int NT_STATUS_NO_SUCH_FILE = 0xC000000f; public static final int NT_STATUS_ACCESS_DENIED = 0xC0000022; @@ -68,6 +69,7 @@ public interface NtStatus { NT_STATUS_UNSUCCESSFUL, NT_STATUS_NOT_IMPLEMENTED, NT_STATUS_INVALID_INFO_CLASS, + NT_STATUS_ACCESS_VIOLATION, NT_STATUS_INVALID_HANDLE, NT_STATUS_NO_SUCH_FILE, NT_STATUS_ACCESS_DENIED, @@ -109,6 +111,7 @@ public interface NtStatus { "A device attached to the system is not functioning.", "Incorrect function.", "The parameter is incorrect.", + "Invalid access to memory location.", "The handle is invalid.", "The system cannot find the file specified.", "Access is denied.", diff --git a/src/jcifs/smb/NtlmPasswordAuthentication.java b/src/jcifs/smb/NtlmPasswordAuthentication.java index aceb373..a25bf29 100644 --- a/src/jcifs/smb/NtlmPasswordAuthentication.java +++ b/src/jcifs/smb/NtlmPasswordAuthentication.java @@ -51,7 +51,7 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable private static final String DEFAULT_USERNAME = Config.getProperty("jcifs.smb.client.username", "GUEST"); - private static final String DEFAULT_PASSWORD = + static final String DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password", ""); private static final Random RANDOM = new Random(); @@ -156,10 +156,12 @@ public final class NtlmPasswordAuthentication implements Principal, Serializable } } -public static final NtlmPasswordAuthentication NULL = + static final NtlmPasswordAuthentication NULL = new NtlmPasswordAuthentication( "", "", "" ); static final NtlmPasswordAuthentication GUEST = new NtlmPasswordAuthentication( "?", "GUEST", "" ); + static final NtlmPasswordAuthentication DEFAULT = + new NtlmPasswordAuthentication( null ); String domain; String username; @@ -168,6 +170,7 @@ public static final NtlmPasswordAuthentication NULL = byte[] unicodeHash; boolean hashesExternal = false; byte[] clientChallenge = null; + byte[] challenge = null; /** * Create an NtlmPasswordAuthentication object from the userinfo @@ -221,14 +224,15 @@ public static final NtlmPasswordAuthentication NULL = * class which is in turn used by NTLM HTTP authentication functionality. */ public NtlmPasswordAuthentication( String domain, String username, - byte[] ansiHash, byte[] unicodeHash ) { + byte[] challenge, byte[] ansiHash, byte[] unicodeHash ) { if( domain == null || username == null || ansiHash == null || unicodeHash == null ) { - throw new IllegalArgumentException( "External credentials cannot null" ); + throw new IllegalArgumentException( "External credentials cannot be null" ); } this.domain = domain; this.username = username; this.password = null; + this.challenge = challenge; this.ansiHash = ansiHash; this.unicodeHash = unicodeHash; hashesExternal = true; diff --git a/src/jcifs/smb/ServerMessageBlock.java b/src/jcifs/smb/ServerMessageBlock.java index 833c8df..f6ac392 100644 --- a/src/jcifs/smb/ServerMessageBlock.java +++ b/src/jcifs/smb/ServerMessageBlock.java @@ -105,9 +105,8 @@ abstract class ServerMessageBlock { static final boolean USE_BATCHING = Config.getBoolean( "jcifs.smb.client.useBatching", true ); static final String OEM_ENCODING = - Config.getProperty( "jcifs.smb.client.codepage", - Config.getProperty( "jcifs.encoding", - System.getProperty( "file.encoding" ))); + Config.getProperty( "jcifs.encoding", + System.getProperty( "file.encoding" )); static LogStream log = LogStream.getInstance(); @@ -304,6 +303,8 @@ abstract class ServerMessageBlock { boolean verifyFailed; NtlmPasswordAuthentication auth = null; String path; + SigningDigest digest = null; + ServerMessageBlock response; ServerMessageBlock() { flags = (byte)( FLAGS_PATH_NAMES_CASELESS | FLAGS_PATH_NAMES_CANONICALIZED ); @@ -408,6 +409,11 @@ Hexdump.hexdump( System.err, src, 0, 256 ); dstIndex += byteCount; length = dstIndex - start; + + if( digest != null ) { + digest.sign( dst, headerStart, length, this, response ); + } + return length; } int readWireFormat( InputStream in, diff --git a/src/jcifs/smb/SigningDigest.java b/src/jcifs/smb/SigningDigest.java index 1852b76..12c47dc 100644 --- a/src/jcifs/smb/SigningDigest.java +++ b/src/jcifs/smb/SigningDigest.java @@ -21,7 +21,6 @@ public class SigningDigest { private byte[] macSigningKey; private int updates; private int signSequence; - boolean sessionSetup; public SigningDigest( SmbTransport transport, NtlmPasswordAuthentication auth ) throws SmbException { diff --git a/src/jcifs/smb/SmbComSessionSetupAndX.java b/src/jcifs/smb/SmbComSessionSetupAndX.java index 4cc0676..6063a05 100644 --- a/src/jcifs/smb/SmbComSessionSetupAndX.java +++ b/src/jcifs/smb/SmbComSessionSetupAndX.java @@ -35,11 +35,16 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock { private String accountName, primaryDomain; SmbSession session; + NtlmPasswordAuthentication auth; - SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) { + SmbComSessionSetupAndX( SmbSession session, ServerMessageBlock andx ) throws SmbException { super( andx ); command = SMB_COM_SESSION_SETUP_ANDX; this.session = session; + this.auth = session.auth; + if( auth.hashesExternal && auth.challenge != session.transport.server.encryptionKey ) { + throw new SmbAuthException( SmbException.NT_STATUS_ACCESS_VIOLATION ); + } } int getBatchLimit( byte command ) { @@ -49,12 +54,11 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock { int start = dstIndex; if( session.transport.server.security == SECURITY_USER && - ( session.auth.hashesExternal || - session.auth.password.length() > 0 )) { + ( auth.hashesExternal || auth.password.length() > 0 )) { if( session.transport.server.encryptedPasswords ) { // encrypted - accountPassword = session.auth.getAnsiHash( session.transport.server.encryptionKey ); - unicodePassword = session.auth.getUnicodeHash( session.transport.server.encryptionKey ); + accountPassword = auth.getAnsiHash( session.transport.server.encryptionKey ); + unicodePassword = auth.getUnicodeHash( session.transport.server.encryptionKey ); passwordLength = unicodePasswordLength = 24; // fix for win9x clients if (unicodePassword.length == 0) unicodePasswordLength = 0; @@ -62,14 +66,14 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock { throw new RuntimeException( "Plain text passwords are disabled" ); } else if( useUnicode ) { // plain text - String password = session.auth.getPassword(); + String password = auth.getPassword(); accountPassword = new byte[0]; passwordLength = 0; unicodePassword = new byte[(password.length() + 1) * 2]; unicodePasswordLength = writeString( password, unicodePassword, 0 ); } else { // plain text - String password = session.auth.getPassword(); + String password = auth.getPassword(); accountPassword = new byte[(password.length() + 1) * 2]; passwordLength = writeString( password, accountPassword, 0 ); unicodePassword = new byte[0]; @@ -106,12 +110,11 @@ class SmbComSessionSetupAndX extends AndXServerMessageBlock { int writeBytesWireFormat( byte[] dst, int dstIndex ) { int start = dstIndex; - accountName = session.auth.username.toUpperCase(); - primaryDomain = session.auth.domain.toUpperCase(); + accountName = auth.username.toUpperCase(); + primaryDomain = auth.domain.toUpperCase(); if( session.transport.server.security == SECURITY_USER && - ( session.auth.hashesExternal || - session.auth.password.length() > 0 )) { + ( auth.hashesExternal || auth.password.length() > 0 )) { System.arraycopy( accountPassword, 0, dst, dstIndex, passwordLength ); dstIndex += passwordLength; System.arraycopy( unicodePassword, 0, dst, dstIndex, unicodePasswordLength ); diff --git a/src/jcifs/smb/SmbComSessionSetupAndXResponse.java b/src/jcifs/smb/SmbComSessionSetupAndXResponse.java index a379d8b..f4c6e94 100644 --- a/src/jcifs/smb/SmbComSessionSetupAndXResponse.java +++ b/src/jcifs/smb/SmbComSessionSetupAndXResponse.java @@ -26,11 +26,12 @@ import jcifs.util.Hexdump; class SmbComSessionSetupAndXResponse extends AndXServerMessageBlock { - private boolean isLoggedInAsGuest; private String nativeOs = ""; private String nativeLanMan = ""; private String primaryDomain = ""; + boolean isLoggedInAsGuest; + SmbComSessionSetupAndXResponse( ServerMessageBlock andx ) { super( andx ); } diff --git a/src/jcifs/smb/SmbComTransaction.java b/src/jcifs/smb/SmbComTransaction.java index cdc8db5..6896e01 100644 --- a/src/jcifs/smb/SmbComTransaction.java +++ b/src/jcifs/smb/SmbComTransaction.java @@ -63,6 +63,7 @@ abstract class SmbComTransaction extends ServerMessageBlock implements Enumerati static final int NET_SHARE_ENUM = 0x0000; static final int NET_SERVER_ENUM2 = 0x0068; + static final int NET_SERVER_ENUM3 = 0x00D7; static final byte TRANS_PEEK_NAMED_PIPE = (byte)0x23; static final byte TRANS_WAIT_NAMED_PIPE = (byte)0x53; diff --git a/src/jcifs/smb/SmbFile.java b/src/jcifs/smb/SmbFile.java index 4f83144..7a37317 100644 --- a/src/jcifs/smb/SmbFile.java +++ b/src/jcifs/smb/SmbFile.java @@ -34,17 +34,16 @@ import jcifs.netbios.NbtAddress; import java.util.Date; /** - *

* This class represents a resource on an SMB network. Mainly these * resources are files and directories however an SmbFile * may also refer to servers and workgroups. If the resource is a file or * directory the methods of SmbFile follow the behavior of * the well known {@link java.io.File} class. One fundamental difference - * is the usage of a URL scheme* to specify the target file or + * is the usage of a URL scheme [1] to specify the target file or * directory. SmbFile URLs have the following syntax: * *

- *     smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]]
+ *     smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?[param=value[param2=value2[...]]]
  * 
* * This example: @@ -53,27 +52,28 @@ import java.util.Date; * smb://storage15/public/foo.txt * * - * would referece the file foo.txt in the share + * would reference the file foo.txt in the share * public on the server storage15. In addition * to referencing files and directories, jCIFS can also address servers, * and workgroups. -

- * Please note that beginning with jcifs-0.7.0b4 all SMB URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'. This will break older code and you may or may not get an exception. For example: -

-smb://server/share/path/to/dir/  <-- GOOD
-
-smb://server/share/path/to/dir   <-- BAD
-
-To assist you with this, the getName method will now include a slash '/' after the name if the URL refers to a directory (e.g. 'dir/'). - -

-When using the java.net.URL class with 'smb://' URLs it is necessary to first call the static jcifs.Config.registerSmbURLHandler(); method (prior to jcifs-0.7.0b12 it was Class.forName( "jcifs.Config" ); but this is no longer necessary). -

-The userinfo component of the SMB URL (domain;user:pass) must be URL encoded if it contains reserved characters. According to RFC 2396 these characters are non US-ASCII characters and most meta characters however jCIFS will work correctly with anything but '@' which is used to delimit the userinfo component from the server and '%' which is the URL escape character itself. -

-When used in conjunction with the list - * method, this functionality can be usefull for network diagnostics - * tools or "Network Neighborhood" like functionality. The server + *

+ * Important: all SMB URLs that represent + * workgroups, servers, shares, or directories require a trailing slash '/'. + * + *

+ * When using the java.net.URL class with + * 'smb://' URLs it is necessary to first call the static + * jcifs.Config.registerSmbURLHandler(); method. This is required + * to register the SMB protocol handler. + *

+ * The userinfo component of the SMB URL (domain;user:pass) must + * be URL encoded if it contains reserved characters. According to RFC 2396 + * these characters are non US-ASCII characters and most meta characters + * however jCIFS will work correctly with anything but '@' which is used + * to delimit the userinfo component from the server and '%' which is the + * URL escape character itself. + *

+ * The server * component may a traditional NetBIOS name, a DNS name, or IP * address. These name resolution mechanisms and their resolution order * can be changed (See Setting Name @@ -84,9 +84,8 @@ When used in conjunction with the list * JCIFS Properties). Here are some examples of SMB URLs with brief * descriptions of what they do: * - *

* This URL scheme is based largely on the SMB - * Filesharing URL Scheme IETF draft. + *

[1] This URL scheme is based largely on the SMB + * Filesharing URL Scheme IETF draft. * *

* @@ -156,6 +155,15 @@ When used in conjunction with the list * URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'. * * + * + * *
+ * smb://MYGROUP/?SERVER=192.168.10.15 + * SMB URLs support some query string parameters. In this example + * the SERVER parameter is used to override the + * server name service lookup to contact the server 192.168.10.15 + * (presumably known to be a master + * browser) for the server list in workgroup MYGROUP. + *
* *

A second constructor argument may be specified to augment the URL @@ -264,19 +272,31 @@ public class SmbFile extends URLConnection { // share access /** - * When specified as the shareAccess constructor parameter, other SMB clients (including other threads macking calls into jCIFS) will not be permitted to access the target file and will receive "The file is being accessed by another process" message. + * When specified as the shareAccess constructor parameter, + * other SMB clients (including other threads making calls into jCIFS) + * will not be permitted to access the target file and will receive "The + * file is being accessed by another process" message. */ public static final int FILE_NO_SHARE = 0x00; /** - * When specified as the shareAccess constructor parameter, other SMB clients will be permitted to read from the target file while this file is open. This constant may be logically OR'd with other share access flags. + * When specified as the shareAccess constructor parameter, + * other SMB clients will be permitted to read from the target file while + * this file is open. This constant may be logically OR'd with other share + * access flags. */ public static final int FILE_SHARE_READ = 0x01; /** - * When specified as the shareAccess constructor parameter, other SMB clients will be permitted to write to the target file while this file is open. This constant may be logically OR'd with other share access flags. + * When specified as the shareAccess constructor parameter, + * other SMB clients will be permitted to write to the target file while + * this file is open. This constant may be logically OR'd with other share + * access flags. */ public static final int FILE_SHARE_WRITE = 0x02; /** - * When specified as the shareAccess constructor parameter, other SMB clients will be permitted to delete the target file while this file is open. This constant may be logically OR'd with other share access flags. + * When specified as the shareAccess constructor parameter, + * other SMB clients will be permitted to delete the target file while + * this file is open. This constant may be logically OR'd with other share + * access flags. */ public static final int FILE_SHARE_DELETE = 0x04; @@ -289,11 +309,35 @@ public class SmbFile extends URLConnection { static final int O_TRUNC = 0x0002; // file attribute encoding +/** + * A file with this bit on as returned by getAttributes() or set + * with setAttributes() will be read-only + */ public static final int ATTR_READONLY = 0x01; +/** + * A file with this bit on as returned by getAttributes() or set + * with setAttributes() will be hidden + */ public static final int ATTR_HIDDEN = 0x02; +/** + * A file with this bit on as returned by getAttributes() or set + * with setAttributes() will be a system file + */ public static final int ATTR_SYSTEM = 0x04; +/** + * A file with this bit on as returned by getAttributes() is + * a volume + */ public static final int ATTR_VOLUME = 0x08; +/** + * A file with this bit on as returned by getAttributes() is + * a directory + */ public static final int ATTR_DIRECTORY = 0x10; +/** + * A file with this bit on as returned by getAttributes() or set + * with setAttributes() is an archived file + */ public static final int ATTR_ARCHIVE = 0x20; // extended file attribute encoding(others same as above) @@ -398,8 +442,8 @@ public class SmbFile extends URLConnection { * the parent SmbFile. See the description above for examples * of using the second name parameter. * - * @param parent A base SmbFile - * @param child A path string relative to the parent paremeter + * @param context A base SmbFile + * @param name A path string relative to the parent paremeter * @throws MalformedURLException * If the parent and child parameters * do not follow the prescribed syntax @@ -407,7 +451,8 @@ public class SmbFile extends URLConnection { * If the server or workgroup of the context file cannot be determined */ - public SmbFile( SmbFile context, String name ) throws MalformedURLException, UnknownHostException { + public SmbFile( SmbFile context, String name ) + throws MalformedURLException, UnknownHostException { this( context.isWorkgroup0() ? new URL( null, "smb://" + name, Handler.SMB_HANDLER ) : new URL( context.url, name, Handler.SMB_HANDLER ), context.auth ); @@ -427,13 +472,13 @@ public class SmbFile extends URLConnection { */ public SmbFile( String context, String name ) throws MalformedURLException { - this( new URL( new URL( null, context, Handler.SMB_HANDLER ), name, Handler.SMB_HANDLER )); + this( new URL( new URL( null, context, Handler.SMB_HANDLER ), + name, Handler.SMB_HANDLER )); } /** * Constructs an SmbFile representing a resource on an SMB network such * as a file or directory. -The second parameter may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. * * @param url A URL string * @param auth The credentials the client should use for authentication @@ -445,9 +490,12 @@ The second parameter may be constructed explicitly or retreived with HttpSer this( new URL( null, url, Handler.SMB_HANDLER ), auth ); } /** - * Constructs an SmbFile representing a file on an SMB network. -The second parameter may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. -The shareAccess parameter controls what permissions other clients have when trying to access the same file while this instance is still open. This value is either FILE_NO_SHARE or any combination of FILE_SHARE_READ, FILE_SHARE_WRITE, and FILE_SHARE_DELETE logically OR'd together. + * Constructs an SmbFile representing a file on an SMB network. The + * shareAccess parameter controls what permissions other + * clients have when trying to access the same file while this instance + * is still open. This value is either FILE_NO_SHARE or any + * combination of FILE_SHARE_READ, FILE_SHARE_WRITE, + * and FILE_SHARE_DELETE logically OR'd together. * * @param url A URL string * @param auth The credentials the client should use for authentication @@ -468,7 +516,6 @@ The shareAccess parameter controls what permissions other clients have * as a file or directory. The second parameter is a relative path from * the context. See the description above for examples of * using the second name parameter. -The third parameter may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. * * @param context A URL string * @param name A path string relative to the context paremeter @@ -485,9 +532,12 @@ The third parameter may be constructed explicitly or retreived with HttpServ * Constructs an SmbFile representing a resource on an SMB network such * as a file or directory. The second parameter is a relative path from * the context. See the description above for examples of - * using the second name parameter. -The third parameter may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. -The shareAccess parameter controls what permissions other clients have when trying to access the same file while this instance is still open. This value is either FILE_NO_SHARE or any combination of FILE_SHARE_READ, FILE_SHARE_WRITE, and FILE_SHARE_DELETE logically OR'd together. + * using the second name parameter. The shareAccess + * parameter controls what permissions other clients have when trying + * to access the same file while this instance is still open. This + * value is either FILE_NO_SHARE or any combination + * of FILE_SHARE_READ, FILE_SHARE_WRITE, and + * FILE_SHARE_DELETE logically OR'd together. * * @param context A URL string * @param name A path string relative to the context paremeter @@ -516,7 +566,8 @@ The shareAccess parameter controls what permissions other clients have } /** * Constructs an SmbFile representing a resource on an SMB network such - * as a file or directory from a URL object and an NtlmPasswordAuthentication object which may be constructed explicitly or retreived with HttpServletRequest.getUserPrincipal() if NTLM HTTP authentication has been successfully negotiated. + * as a file or directory from a URL object and an + * NtlmPasswordAuthentication object. * * @param url The URL of the target resource * @param auth The credentials the client should use for authentication @@ -548,7 +599,10 @@ The shareAccess parameter controls what permissions other clients have } else { this.unc = context.unc + '\\' + name; } - this.type = type == TYPE_WORKGROUP ? 0 : type; + /* why? am I going around in circles? + * this.type = type == TYPE_WORKGROUP ? 0 : type; + */ + this.type = type; this.attributes = attributes; this.createTime = createTime; this.lastModified = lastModified; @@ -644,9 +698,48 @@ The shareAccess parameter controls what permissions other clients have } } + static String queryLookup( String query, String param ) { + char in[] = query.toCharArray(); + int i, ch, st, eq; + + st = eq = 0; + for( i = 0; i < in.length; i++) { + ch = in[i]; + if( ch == '&' ) { + if( eq > st ) { + String p = new String( in, st, eq - st ); + if( p.equalsIgnoreCase( param )) { + eq++; + return new String( in, eq, i - eq ); + } + } + st = i + 1; + } else if( ch == '=' ) { + eq = i; + } + } + if( eq > st ) { + String p = new String( in, st, eq - st ); + if( p.equalsIgnoreCase( param )) { + eq++; + return new String( in, eq, in.length - eq ); + } + } + + return null; + } + UniAddress getAddress() throws UnknownHostException { String host = url.getHost(); String path = url.getPath(); + String query = url.getQuery(); + + if( query != null ) { + String server = queryLookup( query, "server" ); + if( server != null && server.length() > 0 ) { + return UniAddress.getByName( server ); + } + } if( host.length() == 0 ) { return UniAddress.getByName( NbtAddress.getByName( @@ -668,6 +761,10 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( "Failed to connect to server", ioe ); } } +/** + * It is not necessary to call this method directly. This is the + * URLConnection implementation of connect(). + */ public void connect() throws IOException { SmbTransport trans; SmbSession ssn; @@ -693,7 +790,8 @@ The shareAccess parameter controls what permissions other clients have ssn = trans.getSmbSession( NtlmPasswordAuthentication.NULL ); tree = ssn.getSmbTree( null, null ); tree.treeConnect( null, null ); - } else if(( a = NtlmAuthenticator.requestNtlmPasswordAuthentication( url.toString(), sae )) != null ) { + } else if(( a = NtlmAuthenticator.requestNtlmPasswordAuthentication( + url.toString(), sae )) != null ) { auth = a; ssn = trans.getSmbSession( auth ); tree = ssn.getSmbTree( share, null ); @@ -715,11 +813,13 @@ The shareAccess parameter controls what permissions other clients have log.println( "open0: " + unc ); /* - * Open AndX Request / Response + * NT Create AndX / Open AndX Request / Response */ + if( tree.session.transport.hasCapability( ServerMessageBlock.CAP_NT_SMBS )) { SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse(); - send( new SmbComNTCreateAndX( unc, flags, shareAccess, attrs, options, null ), response ); + send( new SmbComNTCreateAndX( unc, flags, shareAccess, + attrs, options, null ), response ); f = response.fid; attributes = response.extFileAttributes & ATTR_GET_MASK; attrExpiration = System.currentTimeMillis() + attrExpirationPeriod; @@ -772,8 +872,7 @@ The shareAccess parameter controls what permissions other clients have * the root URL smb:// is also smb://. If this * SmbFile refers to a workgroup, server, share, or directory, * the name will include a trailing slash '/' so that composing new - * SmbFiles will maintain the trailing slash requirement introduced - * in jcifs-0.7.0b4. + * SmbFiles will maintain the trailing slash requirement. * * @return The last component of the URL associated with this SMB * resource or smb:// if the resource is smb:// @@ -851,11 +950,13 @@ The shareAccess parameter controls what permissions other clients have if( unc == null ) { char[] in = url.getPath().toCharArray(); char[] out = new char[in.length]; - int i, o, state, s; + int length = in.length, i, o, state, s; + /* The canonicalization routine + */ state = 0; o = 0; - for( i = 0; i < in.length; i++ ) { + for( i = 0; i < length; i++ ) { switch( state ) { case 0: if( in[i] != '/' ) { @@ -868,13 +969,13 @@ The shareAccess parameter controls what permissions other clients have if( in[i] == '/' ) { break; } else if( in[i] == '.' && - (( i + 1 ) >= in.length || in[i + 1] == '/' )) { + (( i + 1 ) >= length || in[i + 1] == '/' )) { i++; break; - } else if(( i + 1 ) < in.length && + } else if(( i + 1 ) < length && in[i] == '.' && in[i + 1] == '.' && - (( i + 2 ) >= in.length || in[i + 2] == '/' )) { + (( i + 2 ) >= length || in[i + 2] == '/' )) { i += 2; if( o == 1 ) break; do { @@ -947,8 +1048,8 @@ The shareAccess parameter controls what permissions other clients have /** * Retrieves the share associated with this SMB resource. In - * the case of smb://, smb://workgroup, - * and smb://server URLs which do not specify a share, + * the case of smb://, smb://workgroup/, + * and smb://server/ URLs which do not specify a share, * null will be returned. * * @return The share component or null if there is no share @@ -1172,7 +1273,7 @@ The shareAccess parameter controls what permissions other clients have * Tests to see if the file this SmbFile represents * exists and is not marked read-only. By default, resources are * considered to be read-only and therefore for smb://, - * smb://workgroup, and smb://server resources + * smb://workgroup/, and smb://server/ resources * will be read-only. * * @return true if the resource exists is not marked @@ -1215,7 +1316,9 @@ The shareAccess parameter controls what permissions other clients have } /** - * Tests to see if the file this SmbFile represents is marked as hidden. + * Tests to see if the file this SmbFile represents is marked as + * hidden. This method will also return true for shares with names that + * end with '$' such as IPC$ or C$. * * @return true if the SmbFile is marked as being hidden */ @@ -1234,7 +1337,9 @@ The shareAccess parameter controls what permissions other clients have } /** - * Retrieves the DFS path or null if the path specified does not fall within a DFS volume. + * If the path of this SmbFile falls within a DFS volume, + * this method will return the referral path to which it maps. Otherwise + * null is returned. */ public String getDfsPath() throws SmbException { @@ -1249,9 +1354,9 @@ The shareAccess parameter controls what permissions other clients have } /** - * Retrieve the time this SmbFile was created. The value returned is suitable - * for constructing a {@link java.util.Date} object and is adjusted for - * the servers timezone differential. Times should be the same as those + * Retrieve the time this SmbFile was created. The value + * returned is suitable for constructing a {@link java.util.Date} object + * (i.e. seconds since Epoch 1970). Times should be the same as those * reported using the properties dialog of the Windows Explorer program. * * For Win95/98/Me this is actually the last write time. It is currently @@ -1260,7 +1365,6 @@ The shareAccess parameter controls what permissions other clients have * @return The number of milliseconds since the 00:00:00 GMT, January 1, * 1970 as a long value */ - public long createTime() throws SmbException { if( getUncPath0().length() > 1 ) { exists(); @@ -1270,15 +1374,14 @@ The shareAccess parameter controls what permissions other clients have } /** * Retrieve the last time the file represented by this - * SmbFile was modified. The value returned is suitable - * for constructing a {@link java.util.Date} object and is adjusted for - * the servers timezone differential. Times should be the same as those - * reported using the properties dialog of the Windows Explorer program. + * SmbFile was modified. The value returned is suitable for + * constructing a {@link java.util.Date} object (i.e. seconds since Epoch + * 1970). Times should be the same as those reported using the properties + * dialog of the Windows Explorer program. * * @return The number of milliseconds since the 00:00:00 GMT, January 1, * 1970 as a long value */ - public long lastModified() throws SmbException { if( getUncPath0().length() > 1 ) { exists(); @@ -1286,7 +1389,6 @@ The shareAccess parameter controls what permissions other clients have } return 0L; } - /** * List the contents of this SMB resource. The list returned by this * method will be; @@ -1297,10 +1399,10 @@ The shareAccess parameter controls what permissions other clients have *

  • all available NetBIOS workgroups or domains if this resource is * the top level URL smb://, *
  • all servers registered as members of a NetBIOS workgroup if this - * resource refers to a workgroup in a smb://workgroup URL, + * resource refers to a workgroup in a smb://workgroup/ URL, *
  • all browseable shares of a server including printers, IPC * services, or disk volumes if this resource is a server URL in the form - * smb://server, + * smb://server/, *
  • or null if the resource cannot be resolved. * * @@ -1308,10 +1410,19 @@ The shareAccess parameter controls what permissions other clients have * workgroups, servers, or shares depending on the context of the * resource URL */ - public String[] list() throws SmbException { return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); } + +/** + * List the contents of this SMB resource. The list returned will be + * identical to the list returned by the parameterless list() + * method minus filenames filtered by the specified filter. + * + * @param filter a filename filter to exclude filenames from the results + * @throws SmbException + # @return An array of filenames + */ public String[] list( SmbFilenameFilter filter ) throws SmbException { return list( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null ); } @@ -1330,10 +1441,10 @@ The shareAccess parameter controls what permissions other clients have *
  • all available NetBIOS workgroups or domains if this resource is * the top level URL smb://, *
  • all servers registered as members of a NetBIOS workgroup if this - * resource refers to a workgroup in a smb://workgroup URL, + * resource refers to a workgroup in a smb://workgroup/ URL, *
  • all browseable shares of a server including printers, IPC * services, or disk volumes if this resource is a server URL in the form - * smb://server, + * smb://server/, *
  • or null if the resource cannot be resolved. * * @@ -1341,7 +1452,6 @@ The shareAccess parameter controls what permissions other clients have * and directories, workgroups, servers, or shares depending on the context * of the resource URL */ - public SmbFile[] listFiles() throws SmbException { return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); } @@ -1349,7 +1459,7 @@ The shareAccess parameter controls what permissions other clients have /** * The CIFS protocol provides for DOS "wildcards" to be used as * a performance enhancement. The client does not have to filter - * the names ane the server does not have to return all directory + * the names and the server does not have to return all directory * entries. *

    * The wildcard expression may consist of two special meta @@ -1378,9 +1488,26 @@ The shareAccess parameter controls what permissions other clients have public SmbFile[] listFiles( String wildcard ) throws SmbException { return listFiles( wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); } +/** + * List the contents of this SMB resource. The list returned will be + * identical to the list returned by the parameterless listFiles() + * method minus files filtered by the specified filename filter. + * + * @param filter a filter to exclude files from the results + * @return An array of SmbFile objects + * @throws SmbException + */ public SmbFile[] listFiles( SmbFilenameFilter filter ) throws SmbException { return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null ); } +/** + * List the contents of this SMB resource. The list returned will be + * identical to the list returned by the parameterless listFiles() + * method minus filenames filtered by the specified filter. + * + * @param filter a file filter to exclude files from the results + * @return An array of SmbFile objects + */ public SmbFile[] listFiles( SmbFileFilter filter ) throws SmbException { return listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter ); } @@ -1433,12 +1560,14 @@ The shareAccess parameter controls what permissions other clients have String wildcard, int searchAttributes, SmbFilenameFilter fnf, - SmbFileFilter ff ) throws SmbException, UnknownHostException, MalformedURLException { + SmbFileFilter ff ) throws SmbException, + UnknownHostException, MalformedURLException { SmbComTransaction req; SmbComTransactionResponse resp; int listType = url.getAuthority().length() == 0 ? 0 : getType(); + String p = url.getPath(); - if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { + if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) { throw new SmbException( url.toString() + " directory must end with '/'" ); } @@ -1461,33 +1590,40 @@ The shareAccess parameter controls what permissions other clients have throw new SmbException( "The requested list operations is invalid: " + url.toString() ); } - sendTransaction( req, resp ); - - if( resp.status != SmbException.ERROR_SUCCESS && - resp.status != SmbException.ERROR_MORE_DATA ) { - throw new SmbException( resp.status, true ); - } - - for( int i = 0; i < resp.numEntries; i++ ) { - FileEntry e = resp.results[i]; - String name = e.getName(); - if( fnf != null && fnf.accept( this, name ) == false ) { - continue; + do { + sendTransaction( req, resp ); + + if( resp.status != SmbException.ERROR_SUCCESS && + resp.status != SmbException.ERROR_MORE_DATA ) { + throw new SmbException( resp.status, true ); } - if( name.length() > 0 ) { - SmbFile f = new SmbFile( this, name, - listType == 0 ? TYPE_WORKGROUP : listType, - ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L ); - if( ff != null && ff.accept( f ) == false ) { + + for( int i = 0; i < resp.numEntries; i++ ) { + FileEntry e = resp.results[i]; + String name = e.getName(); + if( fnf != null && fnf.accept( this, name ) == false ) { continue; } - if( files ) { - list.add( f ); - } else { - list.add( name ); + if( name.length() > 0 ) { + SmbFile f = new SmbFile( this, name, + listType == 0 ? TYPE_WORKGROUP : (listType << 1), + ATTR_READONLY | ATTR_DIRECTORY, 0L, 0L, 0L ); + if( ff != null && ff.accept( f ) == false ) { + continue; + } + if( files ) { + list.add( f ); + } else { + list.add( name ); + } } } - } + if( listType != 0 || listType != TYPE_WORKGROUP ) { + break; + } + req.subCommand = (byte)SmbComTransaction.NET_SERVER_ENUM3; + req.reset( 0, ((NetServerEnum2Response)resp).lastName ); + } while( resp.status == SmbException.ERROR_MORE_DATA ); } void doFindFirstNext( ArrayList list, boolean files, @@ -1499,8 +1635,9 @@ The shareAccess parameter controls what permissions other clients have Trans2FindFirst2Response resp; int sid; String path = getUncPath0(); + String p = url.getPath(); - if( url.toString().lastIndexOf( '/' ) != ( url.toString().length() - 1 )) { + if( p.lastIndexOf( '/' ) != ( p.length() - 1 )) { throw new SmbException( url.toString() + " directory must end with '/'" ); } @@ -1561,17 +1698,18 @@ The shareAccess parameter controls what permissions other clients have /** * Changes the name of the file this SmbFile represents to the name - * designated by the SmbFile argument(Remember: - * SmbFiles are immutible - * and therefore the path associated with this SmbFile object will not - * change). + * designated by the SmbFile argument. + *

    + * Remember: SmbFiles are immutible and therefore + * the path associated with this SmbFile object will not + * change). To access the renamed file it is necessary to construct a + * new SmbFile. * * @param dest An SmbFile that represents the new pathname * @return true if the file or directory was successfully renamed * @throws NullPointerException * If the dest argument is null */ - public void renameTo( SmbFile dest ) throws SmbException { if( getUncPath0().length() == 1 || dest.getUncPath0().length() == 1 ) { throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); @@ -1610,7 +1748,7 @@ The shareAccess parameter controls what permissions other clients have class WriterThread extends Thread { byte[] b; int n, off; - boolean ready = true; + boolean ready; SmbFile dest; SmbException e = null; boolean useNTSmbs; @@ -1628,6 +1766,7 @@ The shareAccess parameter controls what permissions other clients have req = new SmbComWrite(); resp = new SmbComWriteResponse(); } + ready = false; } synchronized void write( byte[] b, int n, SmbFile dest, int off ) { @@ -1643,6 +1782,7 @@ The shareAccess parameter controls what permissions other clients have synchronized( this ) { try { for( ;; ) { + notify(); ready = true; while( ready ) { wait(); @@ -1657,7 +1797,6 @@ The shareAccess parameter controls what permissions other clients have req.setParam( dest.fid, off, n, b, 0, n ); dest.send( req, resp ); } - notify(); } } catch( SmbException e ) { this.e = e; @@ -1776,12 +1915,20 @@ The shareAccess parameter controls what permissions other clients have } } /** - * This method will copy the file or directory and it's subcontents - * represented by this SmbFile to the location specified by the + * This method will copy the file or directory represented by this + * SmbFile and it's sub-contents to the location specified by the * dest parameter. This file and the destination file do not * need to be on the same host. This operation does not copy extended - * file attibutes such as ACLs but regular attributes and create and - * last write times will be preserved. + * file attibutes such as ACLs but it does copy regular attributes as + * well as create and last write times. This method is almost twice as + * efficient as manually copying as it employs an additional write + * thread to read and write data concurrently. + *

    + * It is not possible (nor meaningful) to copy entire workgroups or + * servers. + * + * @param dest the destination file or directory + * @throw SmbException */ public void copyTo( SmbFile dest ) throws SmbException { SmbComReadAndX req; @@ -1793,7 +1940,7 @@ The shareAccess parameter controls what permissions other clients have /* Should be able to copy an entire share actually */ if( share == null || dest.share == null) { - throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); + throw new SmbException( "Invalid operation for workgroups or servers" ); } req = new SmbComReadAndX(); @@ -1832,11 +1979,11 @@ The shareAccess parameter controls what permissions other clients have * This method will delete the file or directory specified by this * SmbFile. If the target is a directory, the contents of * the directory will be deleted as well. If a file within the directory or - * it's sub-directories is marked read-only, the read-only status will be removed and the file will be deleted. + * it's sub-directories is marked read-only, the read-only status will + * be removed and the file will be deleted. * * @throws SmbException */ - public void delete() throws SmbException { if( tree == null || tree.inDfs ) { exists(); /* This is necessary to ensure we @@ -1882,7 +2029,8 @@ The shareAccess parameter controls what permissions other clients have /* Recursively delete directory contents */ - SmbFile[] l = listFiles( "*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); + SmbFile[] l = listFiles( "*", + ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null ); for( int i = 0; i < l.length; i++ ) { l[i].delete(); @@ -1897,11 +2045,14 @@ The shareAccess parameter controls what permissions other clients have } /** - * Returns the length of this SmbFile in bytes. - * If this object is a TYPE_SHARE the total capacity of the disk shared in bytes is returned. - * If this object is a directory or a type other than TYPE_SHARE, 0L is returned. + * Returns the length of this SmbFile in bytes. If this object + * is a TYPE_SHARE the total capacity of the disk shared in + * bytes is returned. If this object is a directory or a type other than + * TYPE_SHARE, 0L is returned. * - * @return The length of the file in bytes or 0 if this SmbFile is not a file. + * @return The length of the file in bytes or 0 if this + * SmbFile is not a file. + * @throw SmbException */ public long length() throws SmbException { @@ -1933,6 +2084,9 @@ The shareAccess parameter controls what permissions other clients have * represents or the drive on which the directory or file resides. Objects * other than TYPE_SHARE or TYPE_FILESYSTEM will result * in 0L being returned. + * + * @return the free disk space in bytes of the drive on which this file or + * directory resides */ public long getDiskFreeSpace() throws SmbException { if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) { @@ -1954,15 +2108,15 @@ The shareAccess parameter controls what permissions other clients have /** * Creates a directory with the path specified by this - * SmbFile. For this method to be successfull, the target + * SmbFile. For this method to be successful, the target * must not already exist. This method will fail when - * used with smb://, smb://workgroup, - * smb://server, or smb://server/share URLs - * because workgroups, servers, and shares cannot be dynamically created. + * used with smb://, smb://workgroup/, + * smb://server/, or smb://server/share/ URLs + * because workgroups, servers, and shares cannot be dynamically created + * (although in the future it may be possible to create shares). * * @throws SmbException */ - public void mkdir() throws SmbException { String path = getUncPath0(); @@ -1981,13 +2135,14 @@ The shareAccess parameter controls what permissions other clients have attrExpiration = sizeExpiration = 0; } + /** - * Creates a directory with the path specified by this SmbFile and any parent directories if necessary. - * For this method to be successfull, the target - * must not already exist. This method will fail when - * used with smb://, smb://workgroup, - * smb://server, or smb://server/share URLs - * because workgroups, servers, and shares cannot be dynamically created. + * Creates a directory with the path specified by this SmbFile + * and any parent directories that do not exist. This method will fail + * when used with smb://, smb://workgroup/, + * smb://server/, or smb://server/share/ URLs + * because workgroups, servers, and shares cannot be dynamically created + * (although in the future it may be possible to create shares). * * @throws SmbException */ @@ -2005,6 +2160,11 @@ The shareAccess parameter controls what permissions other clients have mkdir(); } +/** + * Create a new file but fail if it already exists. The check for + * existance of the file and it's creation are an atomic operation with + * respect to other filesystem activities. + */ public void createNewFile() throws SmbException { if( getUncPath0().length() == 1 ) { throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); @@ -2027,6 +2187,15 @@ The shareAccess parameter controls what permissions other clients have 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 + * createTime() method. + *

    + * This method does not apply to workgroups, servers, or shares. + * + * @param time the create time as milliseconds since Jan 1, 1970 + */ public void setCreateTime( long time ) throws SmbException { if( getUncPath0().length() == 1 ) { throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); @@ -2034,6 +2203,15 @@ The shareAccess parameter controls what permissions other clients have setPathInformation( 0, 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 + * lastModified(), getLastModified(), and getDate() methods. + *

    + * This method does not apply to workgroups, servers, or shares. + * + * @param time the last modified time as milliseconds since Jan 1, 1970 + */ public void setLastModified( long time ) throws SmbException { if( getUncPath0().length() == 1 ) { throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); @@ -2042,6 +2220,15 @@ The shareAccess parameter controls what permissions other clients have setPathInformation( 0, 0L, time ); } +/** + * Return the attributes of this file. Attributes are represented as a + * bitset that must be masked with ATTR_* constants to determine + * if they are set or unset. The value returned is suitable for use with + * the setAttributes() method. + * + * @return the ATTR_* attributes associated with this file + * @throw SmbException + */ public int getAttributes() throws SmbException { if( getUncPath0().length() == 1 ) { return 0; @@ -2050,6 +2237,13 @@ The shareAccess parameter controls what permissions other clients have return attributes & ATTR_GET_MASK; } +/** + * Set the attributes of this file. Attributes are composed into a + * bitset by bitwise ORing the ATTR_* constants. Setting the + * value returned by getAttributes will result in both files + * having the same attributes. + * @throw SmbException + */ public void setAttributes( int attrs ) throws SmbException { if( getUncPath0().length() == 1 ) { throw new SmbException( "Invalid operation for workgroups, servers, or shares" ); @@ -2058,10 +2252,22 @@ The shareAccess parameter controls what permissions other clients have setPathInformation( attrs & ATTR_SET_MASK, 0L, 0L ); } +/** + * Make this file read-only. This is shorthand for setAttributes( + * getAttributes() | ATTR_READ_ONLY ). + * + * @throw SmbException + */ public void setReadOnly() throws SmbException { setAttributes( getAttributes() | ATTR_READONLY ); } +/** + * Turn off the read-only attribute of this file. This is shorthand for + * setAttributes( getAttributes() & ~ATTR_READONLY ). + * + * @throw SmbException + */ public void setReadWrite() throws SmbException { setAttributes( getAttributes() & ~ATTR_READONLY ); } @@ -2070,13 +2276,12 @@ The shareAccess parameter controls what permissions other clients have * Returns a {@link java.net.URL} for this SmbFile. The * URL may be used as any other URL might to * access an SMB resource. Currently only retrieving data and information - * is supported. + * is supported (i.e. no doOutput). * * @depricated Use getURL() instead - * - * @return A new {@link java.net.URL} for this SmbFile + * @return A new {@link java.net.URL} for this SmbFile + * @throw MalformedURLException */ - public URL toURL() throws MalformedURLException { return url; } @@ -2091,6 +2296,7 @@ The shareAccess parameter controls what permissions other clients have * to make such a determination. * * @return A hashcode for this abstract file + * @throw SmbException */ public int hashCode() { @@ -2110,10 +2316,11 @@ The shareAccess parameter controls what permissions other clients have * resource. More specifically, two SmbFile objects are * equals if their server IP addresses are equal and the canonicalized * representation of their URLs, minus authentication parameters, are - * case insensitivly and lexographically equal. For example, assuming the - * server angus resolves to the 192.168.1.15 - * IP address, the below URLs would result in SmbFiles - * that are equal. + * case insensitivly and lexographically equal. + *

    + * For example, assuming the server angus resolves to the + * 192.168.1.15 IP address, the below URLs would result in + * SmbFiles that are equal. * *

      * smb://192.168.1.15/share/DIR/foo.txt
    @@ -2123,6 +2330,7 @@ The shareAccess parameter controls what permissions other clients have
      * @param   obj Another SmbFile object to compare for equality
      * @return  true if the two objects refer to the same SMB resource
      *          and false otherwise
    + * @throw SmbException
      */
     
         public boolean equals( Object obj ) {
    @@ -2136,6 +2344,7 @@ The shareAccess parameter controls what permissions other clients have
      * as getPath.
      *
      * @return  The original URL representation of this SMB resource
    + * @throw SmbException
      */
     
         public String toString() {
    @@ -2143,6 +2352,11 @@ The shareAccess parameter controls what permissions other clients have
         }
     
     /* URLConnection implementation */
    +/**
    + * This URLConnection method just returns the result of length().
    + *
    + * @return the length of this file or 0 if it refers to a directory
    + */
     
         public int getContentLength() {
             try {
    @@ -2151,6 +2365,12 @@ The shareAccess parameter controls what permissions other clients have
             }
             return 0;
         }
    +
    +/**
    + * This URLConnection method just returns the result of lastModified.
    + *
    + * @return the last modified data as milliseconds since Jan 1, 1970
    + */
         public long getDate() {
             try {
                 return lastModified();
    @@ -2158,6 +2378,12 @@ The shareAccess parameter controls what permissions other clients have
             }
             return 0L;
         }
    +
    +/**
    + * This URLConnection method just returns the result of lastModified.
    + *
    + * @return the last modified data as milliseconds since Jan 1, 1970
    + */
         public long getLastModified() {
             try {
                 return lastModified();
    @@ -2165,6 +2391,12 @@ The shareAccess parameter controls what permissions other clients have
             }
             return 0L;
         }
    +
    +/**
    + * This URLConnection method just returns a new SmbFileInputStream created with this file.
    + *
    + * @throw IOException thrown by SmbFileInputStream constructor
    + */
         public InputStream getInputStream() throws IOException {
             return new SmbFileInputStream( this );
         }
    diff --git a/src/jcifs/smb/SmbSession.java b/src/jcifs/smb/SmbSession.java
    index 91e6548..ec54b9f 100644
    --- a/src/jcifs/smb/SmbSession.java
    +++ b/src/jcifs/smb/SmbSession.java
    @@ -145,6 +145,7 @@ public final class SmbSession {
         }
         void sessionSetup( ServerMessageBlock andx,
                                 ServerMessageBlock andxResponse ) throws SmbException {
    +
     synchronized( transport() ) {
             if( sessionSetup ) {
                 return;
    @@ -152,19 +153,6 @@ synchronized( transport() ) {
     
             transport.negotiate();
     
    -        /* Create SMB signature digest if necessary
    -         */
    -
    -        if(( transport.flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES ) != 0 &&
    -                (transport.dig == null || transport.dig.sessionSetup == false) &&
    -                                         /* Only the first SMB_COM_SESSION_SETUP_ANX
    -                                          * with creds other than NULL initializes
    -                                          * signing */
    -                auth != NtlmPasswordAuthentication.NULL &&
    -                NtlmPasswordAuthentication.NULL.equals( auth ) == false ) {
    -            transport.dig = new SigningDigest( transport, auth );
    -        }
    -
             /*
              * Session Setup And X Request / Response
              */
    @@ -174,13 +162,33 @@ synchronized( transport() ) {
     
             SmbComSessionSetupAndX request = new SmbComSessionSetupAndX( this, andx );
             SmbComSessionSetupAndXResponse response = new SmbComSessionSetupAndXResponse( andxResponse );
    +
    +        /* Create SMB signature digest if necessary
    +         * Only the first SMB_COM_SESSION_SETUP_ANX with creds other than NULL initializes signing.
    +         */
    +        if( transport.isSignatureSetupRequired( auth )) {
    +            if( auth.hashesExternal && NtlmPasswordAuthentication.DEFAULT_PASSWORD != null ) {
    +                /* preauthentication
    +                 */
    +                transport.getSmbSession( NtlmPasswordAuthentication.DEFAULT ).getSmbTree( LOGON_SHARE, null ).treeConnect( null, null );
    +            }
    +            request.digest = new SigningDigest( transport, auth );
    +        }
    +
             request.auth = auth;
             transport.send( request, response );
     
    +        if( response.isLoggedInAsGuest && "GUEST".equals( auth.username )) {
    +            throw new SmbAuthException( NtStatus.NT_STATUS_LOGON_FAILURE );
    +        }
    +
             uid = response.uid;
             sessionSetup = true;
    -        if( transport.dig != null )
    -            transport.dig.sessionSetup = true;
    +
    +        if( request.digest != null ) {
    +            /* success - install the signing digest */
    +            transport.digest = request.digest;
    +        }
     }
         }
         void logoff( boolean inError ) {
    diff --git a/src/jcifs/smb/SmbTransport.java b/src/jcifs/smb/SmbTransport.java
    index 35f5f59..445825a 100644
    --- a/src/jcifs/smb/SmbTransport.java
    +++ b/src/jcifs/smb/SmbTransport.java
    @@ -158,7 +158,7 @@ private static byte[] rcv_buf = new byte[0xFFFF];
         boolean useUnicode = USE_UNICODE;
         String tconHostName;
         ServerData server;
    -    SigningDigest dig;
    +    SigningDigest digest = null;
     
         static synchronized SmbTransport getSmbTransport( UniAddress address, int port ) {
             return getSmbTransport( address, port, LADDR, LPORT );
    @@ -246,6 +246,12 @@ private static byte[] rcv_buf = new byte[0xFFFF];
             }
             return (capabilities & cap) == cap;
         }
    +    boolean isSignatureSetupRequired( NtlmPasswordAuthentication auth ) {
    +        return ( flags2 & ServerMessageBlock.FLAGS2_SECURITY_SIGNATURES ) != 0 &&
    +                digest == null &&
    +                auth != NtlmPasswordAuthentication.NULL &&
    +                NtlmPasswordAuthentication.NULL.equals( auth ) == false;
    +    }
         void ensureOpen() throws IOException {
             if( socket == null ) {
                 Object obj;
    @@ -320,7 +326,7 @@ private static byte[] rcv_buf = new byte[0xFFFF];
                 } catch( IOException ioe ) {
                 }
             }
    -        dig = null;
    +        digest = null;
             in = null;
             out = null;
             socket = null;
    @@ -425,9 +431,9 @@ synchronized( rcv_buf ) {
     
                                 if( response.errorCode != 0 || e.hasMoreElements() == false ) {
                                     ((SmbComTransactionResponse)response).hasMore = false;
    -                                if( dig != null ) {
    +                                if( digest != null ) {
                                         synchronized( outLock ) {
    -                                        dig.verify(rcv_buf, 0, response);
    +                                        digest.verify(rcv_buf, 0, response);
                                         }
                                     }
                                     response.notify();
    @@ -449,10 +455,9 @@ synchronized( rcv_buf ) {
                                         Hexdump.hexdump( log, rcv_buf, 0, Math.min( response.length, 1024 ));
                                     }
                                 }
    -
    -                            if( dig != null && (response.errorCode == 0 || response.command != ServerMessageBlock.SMB_COM_SESSION_SETUP_ANDX )) {
    +                            if( digest != null ) {
                                     synchronized( outLock ) {
    -                                    dig.verify(rcv_buf, 0, response);
    +                                    digest.verify(rcv_buf, 0, response);
                                     }
                                 }
     
    @@ -554,10 +559,9 @@ synchronized( rcv_buf ) {
                         request.mid = mid.mid;
                         ensureOpen();
     synchronized( snd_buf ) {
    +                    request.digest = digest;
    +                    request.response = null;
                         int length = request.writeWireFormat(snd_buf, 4);
    -                    if( dig != null ) {
    -                        dig.sign(snd_buf, 4, length, request, null );
    -                    }
                         out.write(snd_buf, 4, length);
                         out.flush();
     
    @@ -597,10 +601,11 @@ synchronized( snd_buf ) {
                         responseTable.put( mid, response );
                         ensureOpen();
     synchronized( snd_buf ) {
    -                    int length = request.writeWireFormat(snd_buf, 4);
    -                    if( dig != null ) {
    -                        dig.sign( snd_buf, 4, length, request, response );
    +                    if( digest != null ) {
    +                        request.digest = digest;
    +                        request.response = response;
                         }
    +                    int length = request.writeWireFormat(snd_buf, 4);
                         out.write(snd_buf, 4, length);
                         out.flush();
     
    @@ -652,6 +657,7 @@ synchronized( snd_buf ) {
                 case NtStatus.NT_STATUS_INVALID_WORKSTATION:
                 case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
                 case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
    +            case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
                     throw new SmbAuthException( response.errorCode );
                 case NtStatus.NT_STATUS_PATH_NOT_COVERED:
                     if( request.auth == null ) {
    @@ -694,10 +700,9 @@ synchronized( snd_buf ) {
                             responseTable.put( mid, interimResponse );
                             ensureOpen();
     synchronized(snd_buf) {
    +                        request.digest = digest;
    +                        request.response = response;
                             int length = request.writeWireFormat(snd_buf, 4);
    -                        if( dig != null ) {
    -                            dig.sign(snd_buf, 4, length, request, response);
    -                        }
                             out.write(snd_buf, 4, length);
                             out.flush();
     
    @@ -727,6 +732,7 @@ synchronized(snd_buf) {
                             case NtStatus.NT_STATUS_INVALID_WORKSTATION:
                             case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
                             case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
    +                        case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
                                 throw new SmbAuthException( interimResponse.errorCode );
                             case NtStatus.NT_STATUS_PATH_NOT_COVERED:
                                 if( request.auth == null ) {
    @@ -752,10 +758,9 @@ synchronized(snd_buf) {
                         synchronized( outLock ) {
                             ensureOpen();
     synchronized( snd_buf ) {
    +                        request.digest = digest;
    +                        request.response = response;
                             int length = request.writeWireFormat(snd_buf, 4);
    -                        if( dig != null ) {
    -                            dig.sign(snd_buf, 4, length, request, response);
    -                        }
                             out.write(snd_buf, 4, length);
                             out.flush();
     
    @@ -808,6 +813,7 @@ synchronized( snd_buf ) {
                 case NtStatus.NT_STATUS_INVALID_WORKSTATION:
                 case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
                 case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
    +            case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
                     throw new SmbAuthException( response.errorCode );
                 case NtStatus.NT_STATUS_PATH_NOT_COVERED:
                     if( request.auth == null ) {
    diff --git a/src/jcifs/smb/SmbTree.java b/src/jcifs/smb/SmbTree.java
    index f594435..7f032de 100644
    --- a/src/jcifs/smb/SmbTree.java
    +++ b/src/jcifs/smb/SmbTree.java
    @@ -53,9 +53,10 @@ class SmbTree {
             // transactions are not batchable
             treeConnect( null, null );
             if( service.equals( "A:" ) == false ) {
    -            switch( ((SmbComTransaction)request).subCommand ) {
    +            switch( ((SmbComTransaction)request).subCommand & 0xFF ) {
                     case SmbComTransaction.NET_SHARE_ENUM:
                     case SmbComTransaction.NET_SERVER_ENUM2:
    +                case SmbComTransaction.NET_SERVER_ENUM3:
                     case SmbComTransaction.TRANS_PEEK_NAMED_PIPE:
                     case SmbComTransaction.TRANS_WAIT_NAMED_PIPE:
                     case SmbComTransaction.TRANS_CALL_NAMED_PIPE:
    -- 
    2.11.0