Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jcifs.smb.DfsImpl Maven / Gradle / Ivy
/* jcifs smb client library in Java
* Copyright (C) 2008 "Michael B. Allen"
*
* 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.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import jcifs.CIFSContext;
import jcifs.netbios.UniAddress;
/**
*
*
*/
public class DfsImpl implements Dfs {
private static class CacheEntry {
long expiration;
Map map;
CacheEntry ( long ttl ) {
this.expiration = System.currentTimeMillis() + ttl * 1000L;
this.map = new HashMap<>();
}
}
private static class NegativeCacheEntry extends CacheEntry {
/**
* @param ttl
*/
NegativeCacheEntry ( long ttl ) {
super(ttl);
}
}
private static final Logger log = Logger.getLogger(DfsImpl.class);
private static final String DC_ENTRY = "dc";
private CacheEntry>> _domains = null; /* aka trusted domains cache */
private final Object domainsLock = new Object();
private Map> _dcs = new HashMap<>();
private final Object dcLock = new Object();
private CacheEntry referrals = null;
private final Object referralsLock = new Object();
/**
* @param tc
*
*/
public DfsImpl ( CIFSContext tc ) {}
private Map>> getTrustedDomains ( CIFSContext tf ) throws SmbAuthException {
if ( tf.getConfig().isDfsDisabled() || tf.getCredentials().getUserDomain() == null || tf.getCredentials().getUserDomain().isEmpty() ) {
return null;
}
if ( this._domains != null && System.currentTimeMillis() > this._domains.expiration ) {
this._domains = null;
}
if ( this._domains != null )
return this._domains.map;
try {
String authDomain = tf.getCredentials().getUserDomain();
// otherwise you end up with a wrong server name for kerberos
// seems to be correct according to
// https://lists.samba.org/archive/samba-technical/2009-August/066486.html
// UniAddress addr = UniAddress.getByName(authDomain, true, tf);
// SmbTransport trans = tf.getTransportPool().getSmbTransport(tf, addr, 0);
SmbTransport trans = getDc(tf, authDomain);
CacheEntry>> entry = new CacheEntry<>(tf.getConfig().getDfsTtl() * 10L);
DfsReferral dr = null;
if ( trans != null ) {
// get domain referral
dr = trans.getDfsReferrals(tf, "", 0);
}
if ( dr != null ) {
DfsReferral start = dr;
do {
String domain = dr.server.toLowerCase();
entry.map.put(domain, new HashMap>());
if ( log.isTraceEnabled() ) {
log.trace("Inserting cache entry for domain " + domain + ": " + dr);
}
dr = dr.next;
}
while ( dr != start );
this._domains = entry;
return this._domains.map;
}
}
catch ( IOException ioe ) {
log.debug("getting trusted domains failed: " + tf.getCredentials().getUserDomain(), ioe);
CacheEntry>> entry = new CacheEntry<>(tf.getConfig().getDfsTtl() * 10L);
this._domains = entry;
if ( tf.getConfig().isDfsStrictView() && ioe instanceof SmbAuthException ) {
throw (SmbAuthException) ioe;
}
return this._domains.map;
}
return null;
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.Dfs#isTrustedDomain(jcifs.CIFSContext, java.lang.String)
*/
@Override
public boolean isTrustedDomain ( CIFSContext tf, String domain ) throws SmbAuthException {
synchronized ( this.domainsLock ) {
Map>> domains = getTrustedDomains(tf);
if ( domains == null )
return false;
domain = domain.toLowerCase();
return domains.get(domain) != null;
}
}
private DfsReferral getDcReferrals ( CIFSContext tf, String domain ) throws SmbAuthException {
if ( tf.getConfig().isDfsDisabled() )
return null;
String dom = domain.toLowerCase(Locale.ROOT);
synchronized ( this.dcLock ) {
CacheEntry ce = this._dcs.get(dom);
if ( ce != null && System.currentTimeMillis() > ce.expiration ) {
ce = null;
}
if ( ce != null ) {
return ce.map.get(DC_ENTRY);
}
ce = new CacheEntry<>(tf.getConfig().getDfsTtl());
try {
UniAddress addr = tf.getNameServiceClient().getByName(domain, true);
SmbTransport trans = tf.getTransportPool().getSmbTransport(tf, addr, 0, false);
synchronized ( trans ) {
DfsReferral dr = trans.getDfsReferrals(tf.withAnonymousCredentials(), "\\" + domain, 1);
if ( log.isDebugEnabled() ) {
log.debug("Got DC referral " + dr);
}
ce.map.put(DC_ENTRY, dr);
return dr;
}
}
catch ( IOException ioe ) {
if ( log.isDebugEnabled() ) {
log.debug(String.format("Getting domain controller for %s failed", domain), ioe);
}
ce.map.put(DC_ENTRY, null);
if ( tf.getConfig().isDfsStrictView() && ioe instanceof SmbAuthException ) {
throw (SmbAuthException) ioe;
}
}
ce.map.put(DC_ENTRY, null);
this._dcs.put(dom, ce);
return null;
}
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.Dfs#getDc(jcifs.CIFSContext, java.lang.String)
*/
@Override
public SmbTransport getDc ( CIFSContext tf, String domain ) throws SmbAuthException {
if ( tf.getConfig().isDfsDisabled() )
return null;
try {
DfsReferral dr = getDcReferrals(tf, domain);
if ( dr != null ) {
DfsReferral start = dr;
IOException e = null;
do {
try {
if ( dr.server != null && dr.server.length() > 0 ) {
return tf.getTransportPool().getSmbTransport(
tf,
tf.getNameServiceClient().getByName(dr.server),
0,
false,
!tf.getCredentials().isAnonymous() && tf.getConfig().isIpcSigningEnforced());
}
log.debug("No server name in referral");
return null;
}
catch ( IOException ioe ) {
e = ioe;
}
dr = dr.next;
}
while ( dr != start );
throw e;
}
}
catch ( IOException ioe ) {
if ( log.isDebugEnabled() ) {
log.debug(String.format("Failed to connect to domain controller for %s", domain), ioe);
}
if ( tf.getConfig().isDfsStrictView() && ioe instanceof SmbAuthException ) {
throw (SmbAuthException) ioe;
}
}
return null;
}
/**
* {@inheritDoc}
*
* @see jcifs.smb.Dfs#getReferral(jcifs.CIFSContext, jcifs.smb.SmbTransport, java.lang.String, java.lang.String,
* java.lang.String)
*/
@Override
public DfsReferral getReferral ( CIFSContext tf, SmbTransport trans, String domain, String root, String path ) throws SmbAuthException {
if ( tf.getConfig().isDfsDisabled() )
return null;
String p = "\\" + domain + "\\" + root;
if ( path != null )
p += path;
try {
if ( log.isDebugEnabled() ) {
log.debug("Fetching referral for " + p);
}
DfsReferral dr = trans.getDfsReferrals(tf, p, 0);
if ( dr != null ) {
return dr;
}
}
catch ( IOException ioe ) {
if ( log.isDebugEnabled() ) {
log.debug(String.format("Getting referral for %s failed", p), ioe);
}
if ( tf.getConfig().isDfsStrictView() && ioe instanceof SmbAuthException ) {
throw (SmbAuthException) ioe;
}
}
return null;
}
/**
*
* {@inheritDoc}
*
* @see jcifs.smb.Dfs#resolve(jcifs.CIFSContext, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public DfsReferral resolve ( CIFSContext tf, String domain, String root, String path ) throws SmbAuthException {
if ( tf.getConfig().isDfsDisabled() || root.equals("IPC$") ) {
return null;
}
if ( domain == null ) {
return null;
}
if ( log.isTraceEnabled() ) {
log.trace(String.format("Resolving \\%s\\%s%s", domain, root, path != null ? path : ""));
}
DfsReferral dr = null;
long now = System.currentTimeMillis();
synchronized ( this.domainsLock ) {
/*
* domains that can contain DFS points to maps of roots for each
*/
Map>> domains = getTrustedDomains(tf);
if ( domains != null ) {
if ( log.isTraceEnabled() ) {
for ( Entry>> entry : domains.entrySet() ) {
log.trace("Domain " + entry.getKey());
for ( Entry> entry2 : entry.getValue().entrySet() ) {
log.trace(" Root " + entry2.getKey());
if ( entry2.getValue().map != null ) {
for ( Entry entry3 : entry2.getValue().map.entrySet() ) {
DfsReferral start = entry3.getValue();
DfsReferral r = start;
do {
log.trace(" " + entry3.getKey() + " => " + entry3.getValue());
r = start.next;
}
while ( r != start );
}
}
}
}
}
domain = domain.toLowerCase();
/*
* domain-based DFS root shares to links for each
*/
Map> roots = domains.get(domain);
if ( roots != null ) {
if ( log.isTraceEnabled() ) {
log.trace("Is a domain referral for " + domain);
}
SmbTransport trans = null;
root = root.toLowerCase();
if ( log.isTraceEnabled() ) {
log.trace("Resolving root " + root);
}
/*
* The link entries contain maps of referrals by path representing DFS links.
* Note that paths are relative to the root like "\" and not "\example.com\root".
*/
CacheEntry links = roots.get(root);
if ( links != null && now > links.expiration ) {
if ( log.isDebugEnabled() ) {
log.debug("Removing expired " + links.map);
}
roots.remove(root);
links = null;
}
if ( links == null ) {
log.trace("Loadings links");
if ( ( trans = getDc(tf, domain) ) == null )
return null;
// the tconHostName is from the DC referral, that referral must be resolved
// before following deeper ones. Otherwise e.g. samba will return a broken
// referral.
String refServerName = domain;
synchronized ( trans ) {
try {
// ensure connected
trans.connect();
refServerName = trans.tconHostName;
}
catch ( SmbException e ) {
log.warn("Failed to connect to domain controller", e);
}
dr = getReferral(tf, trans, refServerName, root, path);
}
if ( log.isTraceEnabled() ) {
log.trace("Have referral " + dr);
}
if ( path == null && domain.equals(dr.server) && root.equals(dr.share) ) {
// If we do cache these we never get to the properly cached
// standalone referral we might have.
log.warn("Dropping self-referential referral " + dr);
dr = null;
}
if ( dr != null ) {
int len = 1 + refServerName.length() + 1 + root.length();
links = new CacheEntry<>(tf.getConfig().getDfsTtl());
DfsReferral tmp = dr;
do {
if ( path == null ) {
if ( log.isTraceEnabled() ) {
log.trace("Path is empty, insert root " + tmp);
}
/*
* Store references to the map and key so that
* SmbFile.resolveDfs can re-insert the dr list with
* the dr that was successful so that subsequent
* attempts to resolve DFS use the last successful
* referral first.
*/
tmp.map = links.map;
tmp.key = "\\";
}
tmp.pathConsumed -= len;
tmp = tmp.next;
}
while ( tmp != dr );
if ( log.isDebugEnabled() ) {
log.debug("Have referral " + dr);
}
if ( dr.key != null )
links.map.put(dr.key, dr);
roots.put(root, links);
}
else if ( path == null ) {
roots.put(root, new NegativeCacheEntry(tf.getConfig().getDfsTtl()));
}
}
else if ( links instanceof NegativeCacheEntry ) {
links = null;
}
if ( links != null ) {
String link = "\\";
/*
* Lookup the domain based DFS root target referral. Note the
* path is just "\" and not "\example.com\root".
*/
dr = links.map.get(link);
if ( dr != null && now > dr.expiration ) {
log.trace("Expiring links " + link);
links.map.remove(link);
dr = null;
}
if ( dr == null ) {
if ( trans == null )
if ( ( trans = getDc(tf, domain) ) == null )
return null;
dr = getReferral(tf, trans, domain, root, path);
if ( dr != null ) {
dr.pathConsumed -= 1 + domain.length() + 1 + root.length();
dr.link = link;
if ( log.isTraceEnabled() ) {
log.trace("Have referral " + dr);
}
links.map.put(link, dr);
}
else {
log.trace("No referral found for " + link);
}
}
else {
log.trace("Have cached referral " + dr);
}
}
}
if ( tf.getConfig().isDfsConvertToFQDN() && dr != null ) {
if ( dr.server.indexOf('.') < 0 && dr.server.toUpperCase(Locale.ROOT).equals(dr.server) ) {
String fqdn = dr.server + "." + domain;
if ( log.isDebugEnabled() ) {
log.debug(String.format("Applying DFS netbios name hack %s -> %s ", dr.server, fqdn));
}
dr.server = fqdn;
}
}
}
}
if ( dr == null && path != null ) {
log.debug("No match for domain based root, checking standalone " + domain);
/*
* We did not match a domain based root. Now try to match the
* longest path in the list of stand-alone referrals.
*/
if ( this.referrals != null && now > this.referrals.expiration ) {
this.referrals = null;
}
if ( this.referrals == null ) {
this.referrals = new CacheEntry<>(0);
}
String key = "\\" + domain + "\\" + root;
if ( !path.equals("\\") )
key += path;
key = key.toLowerCase();
Iterator iter = this.referrals.map.keySet().iterator();
while ( iter.hasNext() ) {
String _key = iter.next();
int _klen = _key.length();
boolean match = false;
if ( _klen == key.length() ) {
match = _key.equals(key);
}
else if ( _klen < key.length() ) {
match = _key.regionMatches(0, key, 0, _klen) && key.charAt(_klen) == '\\';
}
if ( match )
dr = this.referrals.map.get(_key);
}
}
return dr;
}
@Override
public synchronized void cache ( CIFSContext tc, String path, DfsReferral dr ) {
int s1, s2;
String server, share, key;
log.debug("Inserting referral for " + path);
if ( tc.getConfig().isDfsDisabled() )
return;
s1 = path.indexOf('\\', 1);
s2 = path.indexOf('\\', s1 + 1);
server = path.substring(1, s1).toLowerCase(Locale.ROOT);
share = path.substring(s1 + 1, s2);
key = path.substring(0, dr.pathConsumed).toLowerCase(Locale.ROOT);
/*
* Samba has a tendency to return referral paths and pathConsumed values
* in such a way that there can be a slash at the end of the path. This
* causes problems matching keys in resolve() where an extra slash causes
* a mismatch. This strips trailing slashes from all keys to eliminate
* this problem.
*/
int ki = key.length();
while ( ki > 1 && key.charAt(ki - 1) == '\\' ) {
ki--;
}
if ( ki < key.length() ) {
key = key.substring(0, ki);
}
if ( tc.getConfig().isDfsConvertToFQDN() ) {
if ( dr.server.indexOf('.') < 0 && dr.server.toUpperCase(Locale.ROOT).equals(dr.server) ) {
if ( server.startsWith(dr.server.toLowerCase(Locale.ROOT) + ".") ) {
if ( log.isDebugEnabled() ) {
log.debug("Adjusting server name " + dr.server + " to " + server);
}
dr.server = server;
}
else {
log.warn("Have unmappable netbios name " + dr.server);
}
}
}
if ( log.isDebugEnabled() ) {
log.debug("Adding key " + key + " to " + dr);
}
/*
* Subtract the server and share from the pathConsumed so that
* it refects the part of the relative path consumed and not
* the entire path.
*/
dr.pathConsumed -= 1 + server.length() + 1 + share.length();
synchronized ( this.referralsLock ) {
if ( this.referrals != null && ( System.currentTimeMillis() + 10000 ) > this.referrals.expiration ) {
this.referrals = null;
}
if ( this.referrals == null ) {
this.referrals = new CacheEntry<>(tc.getConfig().getDfsTtl());
}
this.referrals.map.put(key, dr);
}
}
}