e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
149 lines
7.4 KiB
C#
149 lines
7.4 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="CustomLoaderHelper.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.Hosting {
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Remoting;
|
|
using System.Runtime.Versioning;
|
|
|
|
// Used to locate a custom loader implementation within a bin-deployed assembly.
|
|
|
|
internal sealed class CustomLoaderHelper : MarshalByRefObject {
|
|
|
|
// the first framework version where the custom loader feature was implemented
|
|
private static readonly string _customLoaderTargetFrameworkName = new FrameworkName(".NETFramework", new Version(4, 5, 1)).ToString();
|
|
|
|
private static readonly string _customLoaderAssemblyName = typeof(CustomLoaderHelper).Assembly.FullName;
|
|
private static readonly string _customLoaderTypeName = typeof(CustomLoaderHelper).FullName;
|
|
private static readonly Guid IID_ICustomLoader = new Guid("50A3CE65-2F9F-44E9-9094-32C6C928F966");
|
|
|
|
// Instances of this type should only ever be created via reflection (see call to CreateObjectAndUnwrap
|
|
// in GetCustomLoader).
|
|
private CustomLoaderHelper() { }
|
|
|
|
internal static IObjectHandle GetCustomLoader(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath, string configFilePath, string customLoaderPhysicalPath, out AppDomain newlyCreatedAppDomain) {
|
|
// Step 1: Does the host allow custom loaders?
|
|
|
|
bool? customLoaderIsEnabled = helperFunctions.CustomLoaderIsEnabled;
|
|
if (customLoaderIsEnabled.HasValue) {
|
|
if ((bool)customLoaderIsEnabled) {
|
|
// The custom loader is enabled; move on to the next step.
|
|
}
|
|
else {
|
|
// The custom loader is disabled, fail.
|
|
throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_ForbiddenByHost);
|
|
}
|
|
}
|
|
else {
|
|
// The host hasn't set a policy, so we'll fall back to our default logic of checking the application's trust level.
|
|
if (!IsFullyTrusted(helperFunctions, appConfigMetabasePath)) {
|
|
throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_NotInFullTrust);
|
|
}
|
|
}
|
|
|
|
// Step 2: Create the new AD
|
|
|
|
string binFolderPhysicalPath = helperFunctions.MapPath("/bin/");
|
|
|
|
AppDomainSetup setup = new AppDomainSetup() {
|
|
PrivateBinPathProbe = "*", // disable loading from app base
|
|
PrivateBinPath = binFolderPhysicalPath,
|
|
ApplicationBase = helperFunctions.AppPhysicalPath,
|
|
TargetFrameworkName = _customLoaderTargetFrameworkName
|
|
};
|
|
|
|
if (configFilePath != null) {
|
|
setup.ConfigurationFile = configFilePath;
|
|
}
|
|
|
|
AppDomain newAppDomainForCustomLoader = AppDomain.CreateDomain("aspnet-custom-loader-" + Guid.NewGuid(), null, setup);
|
|
try {
|
|
// Step 3: Instantiate helper in new AD so that we can get a reference to the loader
|
|
CustomLoaderHelper helper = (CustomLoaderHelper)newAppDomainForCustomLoader.CreateInstanceAndUnwrap(_customLoaderAssemblyName, _customLoaderTypeName,
|
|
ignoreCase: false,
|
|
bindingAttr: BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
|
|
binder: null,
|
|
args: null,
|
|
culture: null,
|
|
activationAttributes: null);
|
|
ObjectHandle ohCustomLoader = helper.GetCustomLoaderImpl(customLoaderPhysicalPath);
|
|
|
|
// If we got this far, success!
|
|
newlyCreatedAppDomain = newAppDomainForCustomLoader;
|
|
return ohCustomLoader;
|
|
}
|
|
catch {
|
|
// If something went wrong, kill the new AD.
|
|
AppDomain.Unload(newAppDomainForCustomLoader);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private ObjectHandle GetCustomLoaderImpl(string customLoaderPhysicalPath) {
|
|
// Step 4: Find the implementation in the custom loader assembly
|
|
|
|
// Since we have set the private bin path, we can use this call to Assembly.Load
|
|
// to avoid the load-from context, which has weird behaviors.
|
|
AssemblyName customLoaderAssemblyName = AssemblyName.GetAssemblyName(customLoaderPhysicalPath);
|
|
Assembly customLoaderAssembly = Assembly.Load(customLoaderAssemblyName);
|
|
CustomLoaderAttribute customLoaderAttribute = customLoaderAssembly.GetCustomAttribute<CustomLoaderAttribute>();
|
|
|
|
if (customLoaderAttribute == null) {
|
|
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_NoAttributeFound, customLoaderAssemblyName));
|
|
}
|
|
|
|
// Step 5: Instantiate the custom loader and return a reference back to native code
|
|
|
|
object customLoader = Activator.CreateInstance(customLoaderAttribute.CustomLoaderType);
|
|
|
|
// This check isn't strictly necessary since the unmanaged layer will handle QueryInterface failures
|
|
// appropriately, but we have an opportunity to provide a better error message at this layer.
|
|
if (!ObjectImplementsComInterface(customLoader, IID_ICustomLoader)) {
|
|
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_MustImplementICustomLoader, customLoader.GetType()));
|
|
}
|
|
|
|
return new ObjectHandle(customLoader);
|
|
}
|
|
|
|
private static bool IsFullyTrusted(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath) {
|
|
// The managed configuration system hasn't yet been instantiated but the IIS native config system understands
|
|
// ASP.NET configuration and honors hierarchy and section locking.
|
|
|
|
try {
|
|
// Must exactly match <trust level="Full" />, as this is what ApplicationManager expects.
|
|
string trustLevel = helperFunctions.GetTrustLevel(appConfigMetabasePath);
|
|
return String.Equals("Full", trustLevel, StringComparison.Ordinal);
|
|
}
|
|
catch {
|
|
// If any of the sections are locked or there is a config error, bail.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool ObjectImplementsComInterface(object o, Guid iid) {
|
|
IntPtr pUnknown = IntPtr.Zero;
|
|
IntPtr pInterface = IntPtr.Zero;
|
|
|
|
try {
|
|
pUnknown = Marshal.GetIUnknownForObject(o); // AddRef
|
|
int hr = Marshal.QueryInterface(pUnknown, ref iid, out pInterface); // AddRef
|
|
return (hr == 0 && pInterface != IntPtr.Zero);
|
|
}
|
|
finally {
|
|
if (pUnknown != IntPtr.Zero) {
|
|
Marshal.Release(pUnknown);
|
|
}
|
|
if (pInterface != IntPtr.Zero) {
|
|
Marshal.Release(pInterface);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|