hierynomus / smbj

Server Message Block (SMB2, SMB3) implementation in Java
Other
707 stars 180 forks source link

[How] Is it possible to use SMB URLs? #89

Open PH0lder opened 7 years ago

PH0lder commented 7 years ago

In my investigation of Java support for SMB I checked out a different library (that did not support SMB2 or SMB3, only SMB1) but used smb URLs. Is it possible to use URLs such as:

smb://USERID:PASSWORD@HOSTNAME/SHARENAME

the full spec, copied from elsewhere, being:

      smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?param=value[param2=value2[...]]]

or optionally for something like:

SMBServerList serverList = new ServerList();
serverList.addURL("smb://USERID:PASSWORD@HOSTNAME/SHARENAME");
serverList.addAnonymous("MYDOMAIN","MYSERVER");
File file = serverList.getFile("smb://HOSTNAME/SHARENAME/DirName/FileName");
pepijnve commented 7 years ago

This is not possible at the moment but would be easy to add; at least up to the share part of the URI.

ghost commented 7 years ago

I'm working on getting this implemented. I'm not quite sure how to get the share connected from the SMBClient class, but I'm sure I'll get it figured out. It is being worked on, though :) .

PH0lder commented 7 years ago

If it helps you @codyswanson4 I have a working regular expression for the parsing of a SMB URl, you may feel free to "steal" ;-)

  private final static Pattern pattern = Pattern.compile(
      "(?:(\\w+)(?:[:][/][/]))" +
        "(?:" + "(?:(\\w+)(?:[;]))?" + "(\\w+)" + "(?:(?:[:])(\\w+))?" + "[@]" + ")?" + 
        "(\\w+)" + "(?:(?:[:])(\\d+))?" +
        "[/]" + "(\\w+)?" + "(" + "(?:[/]\\w+)*" + ")" +
        "(?:" + "(?:[?])((\\w+[=]\\w+)" + "([;]\\w+[=]\\w+))*" + ")?"
      );

  public static SMBPathParts parseSMBURL(final String smbURLToParse)
  {
    if (smbURLToParse.isEmpty())
    {
      throw new IllegalArgumentException("parseSMBURL() passed empty URL");
    }

    final Matcher matcher = pattern.matcher(smbURLToParse);
    if (matcher.matches())
    {
      final String protocolPart = matcher.group(1);
      if (protocolPart.isEmpty() || !protocolPart.toUpperCase().startsWith("SMB"))
      {
        throw new IllegalArgumentException("parseSMBURL() passed URL that is not SMB protocol: " + protocolPart);
      }
pepijnve commented 7 years ago

I wouldn't recommend using a regex for this. java.net.URI#create will do the same thing and allow you to extract the various components of the URI.

hierynomus commented 7 years ago

Fully agreed... Using a regex to parse a URL is overly complex as there is a JDK class perfectly suited for this :)

2017-06-22 9:14 GMT+02:00 Pepijn Van Eeckhoudt notifications@github.com:

I wouldn't recommend using a regex for this. java.net.URI#create will do the same thing and allow you to extract the various components of the URI.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-310296360, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHLo0CEXJF3dSkT0yTyBn5juyn-SKNNks5sGhRRgaJpZM4N9gOw .

ghost commented 7 years ago

Ahh, beautiful. I will implement thus using the URI class then. Thanks for mentioning that!

On Thu, Jun 22, 2017, 3:33 AM Jeroen van Erp notifications@github.com wrote:

Fully agreed... Using a regex to parse a URL is overly complex as there is a JDK class perfectly suited for this :)

2017-06-22 9:14 GMT+02:00 Pepijn Van Eeckhoudt notifications@github.com:

I wouldn't recommend using a regex for this. java.net.URI#create will do the same thing and allow you to extract the various components of the URI.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-310296360, or mute the thread < https://github.com/notifications/unsubscribe-auth/AAHLo0CEXJF3dSkT0yTyBn5juyn-SKNNks5sGhRRgaJpZM4N9gOw

.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-310300155, or mute the thread https://github.com/notifications/unsubscribe-auth/AFYQ2JW-eFKdOa93K-wLDoabBYiEtNiSks5sGhi1gaJpZM4N9gOw .

ghost commented 7 years ago

Just an update; I am currently testing the changes to use the host and port parts of the URI. Since the authentication is handled in a separate class, what would be the best way to handle that? I could return both the Connection and Session objects from the connect method as an array, but that would not be compatible with the previous way it is done. Any pointers would be appreciated.

Take a look at my fork for the work done so far: https://github.com/hierynomus/smbj/compare/master...codyswanson4:master

hierynomus commented 7 years ago

I think you should return a File/Directory object (depending on what the URL pointed to). But I also think that the current connect method should remain intact. Using a URL is not a replacement, but an addition.

2017-06-27 19:04 GMT+02:00 Cody Swanson notifications@github.com:

Just an update; I am currently testing the changes to use the host and port parts of the URI. Since the authentication is handled in a separate class, what would be the best way to handle that? I could return both the Connection and Session objects from the connect method as an array, but that would not be compatible with the previous way it is done. Any pointers would be appreciated.

Take a look at my fork for the work done so far: master...codyswanson4:master https://github.com/hierynomus/smbj/compare/master...codyswanson4:master

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-311421992, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHLo_cf1ooEEiOoCcd8173dR8Q5VwBOks5sITYpgaJpZM4N9gOw .

pepijnve commented 7 years ago

I would go for something like this personally (quickly slapped together so pardon the clutter). This supports the common case that people will want to use this for (I think) which is connecting to a share directly without the intermediate steps.

public Share connect(URI uri) throws IOException {
    if (!uri.getScheme().equals("smb")) {
        throw new IOException("Unsupported URI scheme: " + uri.getScheme());
    }

    String host = uri.getHost();
    int port = uri.getPort();
    if (port == -1) {
        port = DEFAULT_PORT;
    }

    AuthenticationContext authContext;
    String userInfo = uri.getUserInfo();
    if (userInfo == null) {
        authContext = AuthenticationContext.anonymous();
    } else {
        String[] userAndPass = userInfo.split(":", 2);
        String user = userAndPass[0];
        String[] userAndDomain = user.split(";", 2);
        String domain, username;
        if (userAndDomain.length == 1) {
            username = userAndDomain[0];
            domain = "";
        } else {
            username = userAndDomain[1];
            domain = userAndDomain[0];
        }
        char[] pass = (userAndPass.length > 1 ? userAndPass[1] : "").toCharArray();
        authContext = new AuthenticationContext(username, pass, domain);
    }

    String path = uri.getPath();
    if (path == null) {
        throw new IOException("Path must be specified");
    }
    String[] pathComponents = path.split("/", 3);
    if (pathComponents.length != 2) {
        throw new IOException("Only one path component may be specified");
    }
    String share = pathComponents[1];

    Connection connection = connect(host, port);
    Session session = connection.authenticate(authContext);
    return session.connectShare(share);
}
pepijnve commented 7 years ago

The object you want to return really depends on whether the path is given in the URI or not and if so how many components were provided. No path -> Session One component -> Share Two or more -> DiskEntry, Pipe, Printer, ...

Not sure what a nice way to handle that is. A typed union kind of return value?

ghost commented 7 years ago

What about introducing a new return object that contains any of the above objects? It would not be backwards compatible, but it may be easier to work with. Any thoughts?

hierynomus commented 7 years ago

Yes, the thought is that would be a terrible api....

Question: what is the usecase for supporting urls? How would people use them?

I think that you should implement a URLHandler. This is a Java SPI point, which would allow you to specify new URL("smb://foo@bar :host/share/dir/file")

Then when you call the getInputStream method on it, it would transparantly connect using smbj and return you the file contents.

Op 4 jul. 2017 5:53 p.m. schreef "Cody Swanson" notifications@github.com:

What about introducing a new return object that contains any of the above objects? It would not be backwards compatible, but it may be easier to work with. Any thoughts?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-312909648, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHLo5QdISba7TnHEk06EVOIpghLaYnlks5sKl_6gaJpZM4N9gOw .

ghost commented 7 years ago

So you're thinking this would get used more as a one time thing like getting a single file from the specified path? I think that is probably what it would get used for, but what was your original use case, @PH0lder ?

hierynomus commented 7 years ago

I actually don't know how useful SMB URLs will be... What I would maybe rather focus on is a Java NIO Files implementation layer so SMB can be used as a regular FileSystem.

PH0lder commented 7 years ago

I'll just leave this here without further comment: https://commons.apache.org/proper/commons-vfs/

PH0lder commented 7 years ago

My original use case was to implement SMB URL browsing, like how it's done in Linux. People seem to already know how these work, for example in Fedora you can say Connect To Server then drop in a URL for FTP or SMB or whatnot, and it seems to work as expected. And, as I posed above, it appears there is Apache Virtual File System (which I didn't know about at the time) but it (experimentally) uses jCIFS which I believe doesn't support SMB2 or SMB3, which I need to support in my use case. Perhaps it would be possible to use SMBj with it and not have to worry about this library directly supporting the URLs... I don't really know. I will be honest that I have been redirected in other directions lately and haven't been paying a lot of attention here.

hierynomus commented 7 years ago

You're right, JCifs has no support for SMBv2 or higher. I don't know how active commons-vfs still is, but a lot of that can now be done using Java NIO. Also Java NIO is also based on URI's and I think contains the right abstraction layer to implement this, without hacking into the library.

2017-07-08 9:41 GMT+02:00 PH0lder notifications@github.com:

My original use case was to implement SMB URL browsing, like how it's done in Linux. People seem to already know how these work, for example in Fedora you can say Connect To Server then drop in a URL for FTP or SMB or whatnot, and it seems to work as expected. And, as I posed above, it appears there is Apache Virtual File System (which I didn't know about at the time) but it (experimentally) uses jCIFS which I believe doesn't support SMB2 or SMB3, which I need to support in my use case. Perhaps it would be possible to use SMBj with it and not have to worry about this library directly supporting the URLs... I don't really know. I will be honest that I have been redirected in other directions lately and haven't been paying a lot of attention here.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-313840907, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHLo85p2S_V4Gvu3sm4u1Q3KdRapuGaks5sLzKkgaJpZM4N9gOw .

ghost commented 7 years ago

That would probably be a better approach than whatever I was doing. Is there already an issue open for implementing NIO?

On Mon, Jul 10, 2017, 3:23 AM Jeroen van Erp notifications@github.com wrote:

You're right, JCifs has no support for SMBv2 or higher. I don't know how active commons-vfs still is, but a lot of that can now be done using Java NIO. Also Java NIO is also based on URI's and I think contains the right abstraction layer to implement this, without hacking into the library.

2017-07-08 9:41 GMT+02:00 PH0lder notifications@github.com:

My original use case was to implement SMB URL browsing, like how it's done in Linux. People seem to already know how these work, for example in Fedora you can say Connect To Server then drop in a URL for FTP or SMB or whatnot, and it seems to work as expected. And, as I posed above, it appears there is Apache Virtual File System (which I didn't know about at the time) but it (experimentally) uses jCIFS which I believe doesn't support SMB2 or SMB3, which I need to support in my use case. Perhaps it would be possible to use SMBj with it and not have to worry about this library directly supporting the URLs... I don't really know. I will be honest that I have been redirected in other directions lately and haven't been paying a lot of attention here.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-313840907, or mute the thread < https://github.com/notifications/unsubscribe-auth/AAHLo85p2S_V4Gvu3sm4u1Q3KdRapuGaks5sLzKkgaJpZM4N9gOw

.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hierynomus/smbj/issues/89#issuecomment-314026233, or mute the thread https://github.com/notifications/unsubscribe-auth/AFYQ2LxxzRNw9Hg12tLJ-T3j0-A-G1ZHks5sMdGBgaJpZM4N9gOw .

hierynomus commented 7 years ago

No such issue open yet.

jansohn commented 5 years ago

This would make switching from jcifs easier. To be honest, I expected SmbPath smbPath = SmbPath.parse(smbUrl); to work when I first tried to use this library. We personally do not work with credentials in the URL so extending the SmbPath.parse(...) would be enough for us.