jcifs.smb.SmbResourceLocatorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-ng Show documentation
Show all versions of jcifs-ng Show documentation
A pure-java CIFS/SMB client library
/*
* © 2017 AgNO3 Gmbh & Co. KG
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package jcifs.smb;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jcifs.Address;
import jcifs.CIFSContext;
import jcifs.CIFSException;
import jcifs.DfsReferralData;
import jcifs.NetbiosAddress;
import jcifs.RuntimeCIFSException;
import jcifs.SmbConstants;
import jcifs.SmbResourceLocator;
import jcifs.internal.util.StringUtil;
import jcifs.netbios.NbtAddress;
import jcifs.netbios.UniAddress;
/**
*
*
* This mainly tracks two locations:
* - canonical URL path: path component of the URL: this is used to reconstruct URLs to resources and is not adjusted by
* DFS referrals. (E.g. a resource with a DFS root's parent will still point to the DFS root not the share it's actually
* located in).
* - share + uncpath within it: This is the relevant information for most SMB requests. Both are adjusted by DFS
* referrals. Nested resources will inherit the information already resolved by the parent resource.
*
* Invariant:
* A directory resource must have a trailing slash/backslash for both URL and UNC path at all times.
*
* @author mbechler
*
*/
class SmbResourceLocatorImpl implements SmbResourceLocatorInternal, Cloneable {
private static final Logger log = LoggerFactory.getLogger(SmbResourceLocatorImpl.class);
private final URL url;
private DfsReferralData dfsReferral = null; // For getDfsPath() and getServerWithDfs()
private String unc; // Initially null; set by getUncPath; never ends with '/'
private String canon; // Initially null; set by getUncPath; dir must end with '/'
private String share; // Can be null
private Address[] addresses;
private int addressIndex;
private int type;
private CIFSContext ctx;
/**
*
* @param ctx
* @param u
*/
public SmbResourceLocatorImpl ( CIFSContext ctx, URL u ) {
this.ctx = ctx;
this.url = u;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#clone()
*/
@Override
protected SmbResourceLocatorImpl clone () {
SmbResourceLocatorImpl loc = new SmbResourceLocatorImpl(this.ctx, this.url);
loc.canon = this.canon;
loc.share = this.share;
loc.dfsReferral = this.dfsReferral;
loc.unc = this.unc;
if ( this.addresses != null ) {
loc.addresses = new UniAddress[this.addresses.length];
System.arraycopy(this.addresses, 0, loc.addresses, 0, this.addresses.length);
}
loc.addressIndex = this.addressIndex;
loc.type = this.type;
return loc;
}
/**
* @param context
* @param name
*/
void resolveInContext ( SmbResourceLocator context, String name ) {
String shr = context.getShare();
if ( shr != null ) {
this.dfsReferral = context.getDfsReferral();
}
int last = name.length() - 1;
boolean trailingSlash = false;
if ( last >= 0 && name.charAt(last) == '/' ) {
trailingSlash = true;
name = name.substring(0, last);
}
if ( shr == null ) {
String[] nameParts = name.split("/");
// server is set through URL, however it's still in the name
int pos = 0;
if ( context.getServer() == null ) {
pos = 1;
}
// first remaining path element would be share
if ( nameParts.length > pos ) {
this.share = nameParts[ pos++ ];
}
// all other remaining path elements are actual path
if ( nameParts.length > pos ) {
String[] remainParts = new String[nameParts.length - pos];
System.arraycopy(nameParts, pos, remainParts, 0, nameParts.length - pos);
this.unc = "\\" + StringUtil.join("\\", remainParts) + ( trailingSlash ? "\\" : "" );
this.canon = "/" + this.share + "/" + StringUtil.join("/", remainParts) + ( trailingSlash ? "/" : "" );
}
else {
this.unc = "\\";
if ( this.share != null ) {
this.canon = "/" + this.share + ( trailingSlash ? "/" : "" );
}
else {
this.canon = "/";
}
}
}
else {
String uncPath = context.getUNCPath();
if ( uncPath.equals("\\") ) {
// context share != null, so the remainder is path
this.unc = '\\' + name.replace('/', '\\') + ( trailingSlash ? "\\" : "" );
this.canon = context.getURLPath() + name + ( trailingSlash ? "/" : "" );
this.share = shr;
}
else {
this.unc = uncPath + name.replace('/', '\\') + ( trailingSlash ? "\\" : "" );
this.canon = context.getURLPath() + name + ( trailingSlash ? "/" : "" );
this.share = shr;
}
}
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getDfsReferral()
*/
@Override
public DfsReferralData getDfsReferral () {
return this.dfsReferral;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getName()
*/
@Override
public String getName () {
String urlpath = getURLPath();
String shr = getShare();
if ( urlpath.length() > 1 ) {
int i = urlpath.length() - 2;
while ( urlpath.charAt(i) != '/' ) {
i--;
}
return urlpath.substring(i + 1);
}
else if ( shr != null ) {
return shr + '/';
}
else if ( this.url.getHost().length() > 0 ) {
return this.url.getHost() + '/';
}
else {
return "smb://";
}
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getParent()
*/
@Override
public String getParent () {
String str = this.url.getAuthority();
if ( str != null && !str.isEmpty() ) {
StringBuffer sb = new StringBuffer("smb://");
sb.append(str);
String urlpath = getURLPath();
if ( urlpath.length() > 1 ) {
sb.append(urlpath);
}
else {
sb.append('/');
}
str = sb.toString();
int i = str.length() - 2;
while ( str.charAt(i) != '/' ) {
i--;
}
return str.substring(0, i + 1);
}
return "smb://";
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getPath()
*/
@Override
public String getPath () {
return this.url.toString();
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getCanonicalURL()
*/
@Override
public String getCanonicalURL () {
String str = this.url.getAuthority();
if ( str != null && !str.isEmpty() ) {
return "smb://" + this.url.getAuthority() + this.getURLPath();
}
return "smb://";
}
@Override
public String getUNCPath () {
if ( this.unc == null ) {
canonicalizePath();
}
return this.unc;
}
@Override
public String getURLPath () {
if ( this.unc == null ) {
canonicalizePath();
}
return this.canon;
}
@Override
public String getShare () {
if ( this.unc == null ) {
canonicalizePath();
}
return this.share;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getServerWithDfs()
*/
@Override
public String getServerWithDfs () {
if ( this.dfsReferral != null ) {
return this.dfsReferral.getServer();
}
return getServer();
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getServer()
*/
@Override
public String getServer () {
String str = this.url.getHost();
if ( str.length() == 0 ) {
return null;
}
return str;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getDfsPath()
*/
@Override
public String getDfsPath () {
if ( this.dfsReferral == null ) {
return null;
}
return "smb:/" + this.dfsReferral.getServer() + "/" + this.dfsReferral.getShare() + this.getUNCPath().replace('\\', '/');
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getPort()
*/
@Override
public int getPort () {
return this.url.getPort();
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getURL()
*/
@Override
public URL getURL () {
return this.url;
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.SmbResourceLocatorInternal#shouldForceSigning()
*/
@Override
public boolean shouldForceSigning () {
return this.ctx.getConfig().isIpcSigningEnforced() && !this.ctx.getCredentials().isAnonymous() && isIPC();
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#isIPC()
*/
@Override
public boolean isIPC () {
String shr = this.getShare();
if ( shr == null || "IPC$".equals(getShare()) ) {
if ( log.isDebugEnabled() ) {
log.debug("Share is IPC " + this.share);
}
return true;
}
return false;
}
/**
* @param t
*/
void updateType ( int t ) {
this.type = t;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#getType()
*/
@Override
public int getType () throws CIFSException {
if ( this.type == 0 ) {
if ( getUNCPath().length() > 1 ) {
this.type = SmbConstants.TYPE_FILESYSTEM;
}
else if ( getShare() != null ) {
if ( getShare().equals("IPC$") ) {
this.type = SmbConstants.TYPE_NAMED_PIPE;
}
else {
this.type = SmbConstants.TYPE_SHARE;
}
}
else if ( this.url.getAuthority() == null || this.url.getAuthority().isEmpty() ) {
this.type = SmbConstants.TYPE_WORKGROUP;
}
else {
try {
NetbiosAddress nbaddr = getAddress().unwrap(NetbiosAddress.class);
if ( nbaddr != null ) {
int code = nbaddr.getNameType();
if ( code == 0x1d || code == 0x1b ) {
this.type = SmbConstants.TYPE_WORKGROUP;
return this.type;
}
}
}
catch ( CIFSException e ) {
if ( ! ( e.getCause() instanceof UnknownHostException ) ) {
throw e;
}
log.debug("Unknown host", e);
}
this.type = SmbConstants.TYPE_SERVER;
}
}
return this.type;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#isWorkgroup()
*/
@Override
public boolean isWorkgroup () throws CIFSException {
if ( this.type == SmbConstants.TYPE_WORKGROUP || this.url.getHost().length() == 0 ) {
this.type = SmbConstants.TYPE_WORKGROUP;
return true;
}
if ( getShare() == null ) {
NetbiosAddress addr = getAddress().unwrap(NetbiosAddress.class);
if ( addr != null ) {
int code = addr.getNameType();
if ( code == 0x1d || code == 0x1b ) {
this.type = SmbConstants.TYPE_WORKGROUP;
return true;
}
}
this.type = SmbConstants.TYPE_SERVER;
}
return false;
}
@Override
public Address getAddress () throws CIFSException {
if ( this.addressIndex == 0 )
return getFirstAddress();
return this.addresses[ this.addressIndex - 1 ];
}
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;
}
Address getFirstAddress () throws CIFSException {
this.addressIndex = 0;
if ( this.addresses == null ) {
String host = this.url.getHost();
String path = this.url.getPath();
String query = this.url.getQuery();
try {
if ( query != null ) {
String server = queryLookup(query, "server");
if ( server != null && server.length() > 0 ) {
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = this.ctx.getNameServiceClient().getByName(server);
}
String address = queryLookup(query, "address");
if ( address != null && address.length() > 0 ) {
byte[] ip = java.net.InetAddress.getByName(address).getAddress();
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = new UniAddress(java.net.InetAddress.getByAddress(host, ip));
}
}
else if ( host.length() == 0 ) {
try {
Address addr = this.ctx.getNameServiceClient().getNbtByName(NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = this.ctx.getNameServiceClient().getByName(addr.getHostAddress());
}
catch ( UnknownHostException uhe ) {
log.debug("Unknown host", uhe);
if ( this.ctx.getConfig().getDefaultDomain() == null ) {
throw uhe;
}
this.addresses = this.ctx.getNameServiceClient().getAllByName(this.ctx.getConfig().getDefaultDomain(), true);
}
}
else if ( path.length() == 0 || path.equals("/") ) {
this.addresses = this.ctx.getNameServiceClient().getAllByName(host, true);
}
else {
this.addresses = this.ctx.getNameServiceClient().getAllByName(host, false);
}
}
catch ( UnknownHostException e ) {
throw new CIFSException("Failed to lookup address for name " + host, e);
}
}
return getNextAddress();
}
Address getNextAddress () {
Address addr = null;
if ( this.addressIndex < this.addresses.length )
addr = this.addresses[ this.addressIndex++ ];
return addr;
}
boolean hasNextAddress () {
return this.addressIndex < this.addresses.length;
}
/**
* {@inheritDoc}
*
* @see jcifs.SmbResourceLocator#isRoot()
*/
@Override
public boolean isRoot () {
// length == 0 should not happen
return getShare() == null && getUNCPath().length() <= 1;
}
boolean isRootOrShare () {
// length == 0 should not happen
return getUNCPath().length() <= 1;
}
/**
* @throws MalformedURLException
*
*/
private synchronized void canonicalizePath () {
char[] in = this.url.getPath().toCharArray();
char[] out = new char[in.length];
int length = in.length, prefixLen = 0, state = 0;
/*
* The canonicalization routine
*/
for ( int i = 0; i < length; i++ ) {
switch ( state ) {
case 0:
if ( in[ i ] != '/' ) {
// Checked exception (e.g. MalformedURLException) would be better
// but this would be a nightmare API wise
throw new RuntimeCIFSException("Invalid smb: URL: " + this.url);
}
out[ prefixLen++ ] = in[ i ];
state = 1;
break;
case 1:
if ( in[ i ] == '/' ) {
break;
}
else if ( in[ i ] == '.' && ( ( i + 1 ) >= length || in[ i + 1 ] == '/' ) ) {
i++;
break;
}
else if ( ( i + 1 ) < length && in[ i ] == '.' && in[ i + 1 ] == '.' && ( ( i + 2 ) >= length || in[ i + 2 ] == '/' ) ) {
i += 2;
if ( prefixLen == 1 )
break;
do {
prefixLen--;
}
while ( prefixLen > 1 && out[ prefixLen - 1 ] != '/' );
break;
}
state = 2;
case 2:
if ( in[ i ] == '/' ) {
state = 1;
}
out[ prefixLen++ ] = in[ i ];
break;
}
}
this.canon = new String(out, 0, prefixLen);
if ( prefixLen > 1 ) {
prefixLen--;
int firstSep = this.canon.indexOf('/', 1);
if ( firstSep < 0 ) {
this.share = this.canon.substring(1);
this.unc = "\\";
}
else if ( firstSep == prefixLen ) {
this.share = this.canon.substring(1, firstSep);
this.unc = "\\";
}
else {
this.share = this.canon.substring(1, firstSep);
this.unc = this.canon.substring(firstSep, prefixLen + 1).replace('/', '\\');
}
}
else {
this.canon = "/";
this.share = null;
this.unc = "\\";
}
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode () {
int hash;
try {
hash = getAddress().hashCode();
}
catch ( CIFSException uhe ) {
hash = getServer().toUpperCase().hashCode();
}
return hash + getURLPath().toUpperCase().hashCode();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals ( Object obj ) {
if ( ! ( obj instanceof SmbResourceLocatorImpl ) ) {
return false;
}
SmbResourceLocatorImpl o = (SmbResourceLocatorImpl) obj;
/*
* If uncertain, pathNamesPossiblyEqual returns true.
* Comparing canonical paths is definitive.
*/
if ( pathNamesPossiblyEqual(this.url.getPath(), o.url.getPath()) ) {
if ( getURLPath().equalsIgnoreCase(o.getURLPath()) ) {
try {
return getAddress().equals(o.getAddress());
}
catch ( CIFSException uhe ) {
log.debug("Unknown host", uhe);
return getServer().equalsIgnoreCase(o.getServer());
}
}
}
return false;
}
private static boolean pathNamesPossiblyEqual ( String path1, String path2 ) {
int p1, p2, l1, l2;
// if unsure return this method returns true
p1 = path1.lastIndexOf('/');
p2 = path2.lastIndexOf('/');
l1 = path1.length() - p1;
l2 = path2.length() - p2;
// anything with dots voids comparison
if ( l1 > 1 && path1.charAt(p1 + 1) == '.' )
return true;
if ( l2 > 1 && path2.charAt(p2 + 1) == '.' )
return true;
return l1 == l2 && path1.regionMatches(true, p1, path2, p2, l1);
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.SmbResourceLocatorInternal#overlaps(jcifs.SmbResourceLocator)
*/
@Override
public boolean overlaps ( SmbResourceLocator other ) throws CIFSException {
String tp = getCanonicalURL();
String op = other.getCanonicalURL();
return getAddress().equals(other.getAddress()) && tp.regionMatches(true, 0, op, 0, Math.min(tp.length(), op.length()));
}
/**
* @param dr
* @param reqPath
* @return UNC path the redirect leads to
*/
@Override
public String handleDFSReferral ( DfsReferralData dr, String reqPath ) {
if ( Objects.equals(this.dfsReferral, dr) ) {
return this.unc;
}
this.dfsReferral = dr;
String oldUncPath = getUNCPath();
int pathConsumed = dr.getPathConsumed();
if ( pathConsumed < 0 ) {
log.warn("Path consumed out of range " + pathConsumed);
pathConsumed = 0;
}
else if ( pathConsumed > this.unc.length() ) {
log.warn("Path consumed out of range " + pathConsumed);
pathConsumed = oldUncPath.length();
}
if ( log.isDebugEnabled() ) {
log.debug("UNC is '" + oldUncPath + "'");
log.debug("Consumed '" + oldUncPath.substring(0, pathConsumed) + "'");
}
String dunc = oldUncPath.substring(pathConsumed);
if ( log.isDebugEnabled() ) {
log.debug("Remaining '" + dunc + "'");
}
if ( dunc.equals("") || dunc.equals("\\") ) {
dunc = "\\";
this.type = SmbConstants.TYPE_SHARE;
}
if ( !dr.getPath().isEmpty() ) {
dunc = "\\" + dr.getPath() + dunc;
}
if ( dunc.charAt(0) != '\\' ) {
log.warn("No slash at start of remaining DFS path " + dunc);
}
this.unc = dunc;
if ( dr.getShare() != null && !dr.getShare().isEmpty() ) {
this.share = dr.getShare();
}
if ( reqPath != null && reqPath.endsWith("\\") && !dunc.endsWith("\\") ) {
dunc += "\\";
}
return dunc;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString () {
StringBuilder sb = new StringBuilder(this.url.toString());
sb.append('[');
if ( this.unc != null ) {
sb.append("unc=");
sb.append(this.unc);
}
if ( this.canon != null ) {
sb.append("canon=");
sb.append(this.canon);
}
if ( this.dfsReferral != null ) {
sb.append("dfsReferral=");
sb.append(this.dfsReferral);
}
sb.append(']');
return sb.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy