Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing SMB2.1 or SMB3 share from Java?

Tags:

java

smb

As Windows 2012R2 no longer supports the SMB 1 protocol without some registry hacks has anyone had any success working with SMB shares in Java where only SMB 2.1 or SMB 3 are supported?

The JCIFS library is apparently SMB 1 only so it is out. I see that Microsoft have a Java library for Azure but this appears to be utilising services rather than SMB.

like image 569
Moth Avatar asked Jan 05 '17 20:01

Moth


People also ask

Is SMB2 the same as SMB3?

The original name for SMB3 (a.k.a. SMB v3) was SMB 2.2. The initial release of SMB3 is now known as SMB 3.0. Server 2012 and Windows 8 were the first Microsoft operating systems to support it. SMB v3 adds more performance and security enhancements to the protocol.

Is SMB3 faster than SMB2?

SMB2 was faster than SMB3. SMB2 gave me about 128-145 MB/sec. SMB3 gave me about 110-125 MB/sec.

How do I enable SMB3 on Windows 10?

Under Control Panel Home, select Turn Windows features on or off to open the Windows Features box. In the Windows Features box, scroll down the list, clear the check box for SMB 1.0/CIFS File Sharing Support and select OK. After Windows applies the change, on the confirmation page, select Restart now.

What is SMB in Java?

The Server Message Block (SMB) is a simple network protocol that lets you transfer files to a shared file server.


2 Answers

I found this package that can work with SMB2 and SMB3, named smbj Take a look at this: https://github.com/hierynomus/smbj

like image 150
Michael Biniashvili Avatar answered Oct 18 '22 22:10

Michael Biniashvili


Expanding on @Breakidi answer, I've just used hierynomus/smbj v0.2.0 on Android and added SMB2 support. It claims support for both SMB2 and SMB3 although classes reference only SMB2 versions, not sure, maybe it is irrelevant.

Testing

I've tested it against box running SMB2 open in one case and then both SMB2 and SMB3 open in another. I could not disable SMB2 and test SMB3 alone though.

Bouncycastle/Spongycastle

There was a need to use Spongycastle in my case (most likely because of Android) as required MD4 dependency was missing from classpath. I've used it within my class that connects to SMB:

import org.spongycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
<...>
static {
    Security.addProvider(new BouncyCastleProvider());
}

Uploading file

Make sure you set correct flags when call openFile on a DiskShare (by looking into source code off course):

// required imports
import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2CreateOptions;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.common.SMBApiException;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashSet;
...
// connection params
String sambaDomain = null; // can be null
String sambaUsername = "iamuploader";
String sambaPass = "mysecret";
String sambaIP = "192.168.1.236";
String sambaSharedPath = "sharedfolder";

...
// upload method
// usage: upload("file/whithin/folder.txt", fileBytes);
public void upload(String filename, byte[] bytes) throws IOException {

    SmbConfig cfg = SmbConfig.builder().build();
    SMBClient client = new SMBClient(cfg);
    Connection connection = client.connect(sambaIP);
    Session session = connection.authenticate(new AuthenticationContext(sambaUsername, sambaPass.toCharArray(), sambaDomain));
    DiskShare share = (DiskShare) session.connectShare(sambaSharedPath);

    // this is com.hierynomus.smbj.share.File !
    File f = null;
    int idx = filename.lastIndexOf("/");

    // if file is in folder(s), create them first
    if(idx > -1) {
        String folder = filename.substring(0, idx);
        try {
            if(!share.folderExists(folder)) share.mkdir(folder);
        } catch (SMBApiException ex) {
            throw new IOException(ex);
        }

    }

    // I am creating file with flag FILE_CREATE, which will throw if file exists already
    if(!share.fileExists(filename)){
        f = share.openFile(filename,
                new HashSet<>(Arrays.asList(AccessMask.GENERIC_ALL)),
                new HashSet<>(Arrays.asList(FileAttributes.FILE_ATTRIBUTE_NORMAL)),
                SMB2ShareAccess.ALL,
                SMB2CreateDisposition.FILE_CREATE,
                new HashSet<>(Arrays.asList(SMB2CreateOptions.FILE_DIRECTORY_FILE))
        );
    }

    if(f == null) return null;

    OutputStream os = f.getOutputStream();
    os.write(bytes);
    os.close();
}
like image 23
Ivar Avatar answered Oct 18 '22 21:10

Ivar