All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hierynomus.msdfsc.ReferralCache Maven / Gradle / Ivy

There is a newer version: 0.13.0
Show newest version
/*
 * Copyright (C)2016 - SMBJ Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hierynomus.msdfsc;

import com.hierynomus.msdfsc.messages.DFSReferral;
import com.hierynomus.msdfsc.messages.SMB2GetDFSReferralResponse;
import com.hierynomus.msdfsc.messages.SMB2GetDFSReferralResponse.ReferralHeaderFlags;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * [MS-DFSC].pdf: 3.1.1 Abstract Data Model
 * 

* ReferralCache: This cache contains root, link, and sysvol referral responses. * A hit on a ReferralCache entry indicates that the path in a name resolution * operation is a DFS Root, DFS link, or a SYSVOL/NETLOGON share. A * ReferralCache entry conceptually contains entries indexed by a DFS path * prefix, DFSPathPrefix. An entry is a tuple of the form . * DFSPathPrefix is the DFS path that corresponds to a DFS root or a DFS link, * and is the same as the string pointed to by the DFSPathOffset of a * DFS_REFERRAL_V2, DFS_REFERRAL_V3 or DFS_REFERRAL_V4 referral entry. * RootOrLink identifies whether the entry contains DFS root targets or DFS link * targets. It reflects the value from the ServerType field of a referral entry * (as specified in sections 2.2.5.1, 2.2.5.2, 2.2.5.3, and 2.2.5.4). Interlink * identifies whether the entry contains a target in another DFS namespace, as * determined by the test in section 3.1.5.4.5. TargetFailback is used only for * DFS_REFERRAL_V4 and contains the value from the TargetFailback bit of the * referral header (as specified in section 2.2.4). TTL contains a value derived * from the TimeToLive field of a referral entry (as specified in sections * 2.2.5.1, 2.2.5.2, 2.2.5.3, and 2.2.5.4). This is the time stamp at which a * ReferralCache entry is considered to be expired. An implementation is free to * come up with soft and hard time-outs based on the TimeToLive field of the * referral entry, for example. The soft time-out can be used to initiate a * ReferralCache entry refresh operation while permitting the use of the * ReferralCache entry; the hard time-out limit can be used to fail any * operation using the ReferralCache entry if all attempts to refresh it * fail.<4> TargetHint identifies a target in TargetList that was last * successfully used by the DFS client. TargetList consists of tuples of the * form , where TargetPath is the string pointed * to by the NetworkAddressOffset field (as specified in sections 2.2.5.2, * 2.2.5.3, and 2.2.5.4). TargetSetBoundary is only present in V4 referrals and * reflects the value from the TargetSetBoundary of the referral entry (as * specified in section 2.2.5.4). */ public class ReferralCache { private ReferralCacheNode cacheRoot = new ReferralCacheNode(""); public static class TargetSetEntry { final String targetPath; final boolean targetSetBoundary; public TargetSetEntry(String targetPath, boolean targetSetBoundary) { this.targetPath = targetPath; this.targetSetBoundary = targetSetBoundary; } public String getTargetPath() { return targetPath; } @Override public String toString() { return "TargetSetEntry[" + targetPath + ",targetSetBoundary=" + targetSetBoundary + "]"; } } public ReferralCacheEntry lookup(DFSPath dfsPath) { List pathComponents = dfsPath.getPathComponents(); ReferralCacheEntry referralEntry = cacheRoot.getReferralEntry(pathComponents.iterator()); return referralEntry; } public void clear(DFSPath dfsPath) { List pathComponents = dfsPath.getPathComponents(); cacheRoot.deleteExpiredReferralEntry(pathComponents); } public void put(ReferralCacheEntry referralCacheEntry) { List pathComponents = new DFSPath(referralCacheEntry.dfsPathPrefix).getPathComponents(); cacheRoot.addReferralEntry(pathComponents.iterator(), referralCacheEntry); } public void clear() { cacheRoot.clear(); } public static class ReferralCacheEntry { private final String dfsPathPrefix; private final DFSReferral.ServerType rootOrLink; private final boolean interlink; private final int ttl; private final long expires; private final boolean targetFailback; private int targetHint = 0; private final List targetList; public ReferralCacheEntry(SMB2GetDFSReferralResponse response, DomainCache domainCache) { List referralEntries = response.getReferralEntries(); for (DFSReferral referralEntry : referralEntries) { if (referralEntry.getPath() == null) { // illegal value for referral cache entry. throw new IllegalStateException("Path cannot be null for a ReferralCacheEntry?"); } } DFSReferral firstReferral = referralEntries.get(0); this.dfsPathPrefix = firstReferral.getDfsPath(); this.rootOrLink = firstReferral.getServerType(); // 3.1.5.4.5 Determining Whether a Referral Response is an Interlink // A referral response is an Interlink if either of the following two conditions holds: // - If the ReferralServers and StorageServers bits of the ReferralHeaderFlags field in the referral header // (as specified in section 2.2.4) are set to 1 and 0 respectively. // - If the TargetList has one entry, and a lookup of the first path component of the TargetList entry // against the DomainCache results in a cache hit, indicating that the path refers to a domain namespace. boolean interlink = response.getReferralHeaderFlags().contains(ReferralHeaderFlags.ReferralServers) && !response.getReferralHeaderFlags().contains(ReferralHeaderFlags.StorageServers); if (!interlink && referralEntries.size() == 1) { List pathEntries = new DFSPath(firstReferral.getPath()).getPathComponents(); interlink = (domainCache.lookup(pathEntries.get(0)) != null); } this.interlink = interlink; this.ttl = firstReferral.getTtl(); this.expires = System.currentTimeMillis() + this.ttl * 1000L; this.targetFailback = response.getReferralHeaderFlags().contains(ReferralHeaderFlags.TargetFailback); List targetList = new ArrayList<>(referralEntries.size()); for (DFSReferral r : referralEntries) { TargetSetEntry e = new TargetSetEntry(r.getPath(), false); targetList.add(e); } this.targetList = Collections.unmodifiableList(targetList); } public boolean isExpired() { long now = System.currentTimeMillis(); return (now > expires); } public boolean isLink() { return rootOrLink == DFSReferral.ServerType.LINK; } public boolean isRoot() { return rootOrLink == DFSReferral.ServerType.ROOT; } public boolean isInterlink() { return isLink() && interlink; } public String getDfsPathPrefix() { return dfsPathPrefix; } public TargetSetEntry getTargetHint() { return targetList.get(targetHint); } public synchronized TargetSetEntry nextTargetHint() { if (targetHint < targetList.size()-1) { targetHint++; return getTargetHint(); } else { return null; } } public List getTargetList() { return targetList; } @Override public String toString() { return dfsPathPrefix + "->" + getTargetHint().targetPath + "(" + rootOrLink + "), " + targetList; } } private static class ReferralCacheNode { static final AtomicReferenceFieldUpdater ENTRY_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ReferralCacheNode.class, ReferralCacheEntry.class, "entry"); private final String pathComponent; private final Map childNodes = new ConcurrentHashMap<>(); private volatile ReferralCacheEntry entry; ReferralCacheNode(String pathComponent) { this.pathComponent = pathComponent; } void addReferralEntry(Iterator pathComponents, ReferralCacheEntry entry) { if (pathComponents.hasNext()) { String component = pathComponents.next().toLowerCase(); ReferralCacheNode referralCacheNode = childNodes.get(component); if (referralCacheNode == null) { childNodes.put(component, (referralCacheNode = new ReferralCacheNode(component))); } referralCacheNode.addReferralEntry(pathComponents, entry); } else { ENTRY_UPDATER.set(this, entry); } } ReferralCacheEntry getReferralEntry(Iterator pathComponents) { if (pathComponents.hasNext()) { String component = pathComponents.next().toLowerCase(); ReferralCacheNode referralCacheNode = childNodes.get(component); if (referralCacheNode != null) { return referralCacheNode.getReferralEntry(pathComponents); } } return ENTRY_UPDATER.get(this); } void deleteExpiredReferralEntry(List pathComponents) { if (this.entry != null && this.entry.isExpired() && !this.entry.isRoot()) { this.clear(); return; } if (pathComponents!=null && !pathComponents.isEmpty()) { String component = pathComponents.get(0).toLowerCase(); ReferralCacheNode referralCacheNode = childNodes.get(component); if (referralCacheNode != null) { referralCacheNode.deleteExpiredReferralEntry(pathComponents.subList(1,pathComponents.size())); } } } void clear() { this.childNodes.clear(); ENTRY_UPDATER.set(this, null); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy