Introduction
The original Cassini project was written and developed by Microsoft in C# to show off the capabilities of the System.Web.Hosting
namespace in .NET. VB.NET developers were left in the dark as Microsoft (or anybody else) has not released a VB.NET version of the source (that I could find). Cassini is a basic Web Server delivery platform that allows developers to test their .NET applications without the need for Internet Information Server to be installed on a development machine or server.
The primary goals of this project were to convert the C# development code to VB.NET so developers can read/use the code in this language, I also wanted to add a reasonable amount of security controls into the code so the server was well protected from internal security threats or viruses/worms roaming around the network.
The demo and source code provided includes a working installation wrapped around a Windows Service. The Server has been named xNetServer (extra .NET Server) and uses an XML file to set the configuration and security options for the service.
The following additional functionality has been included into the xNetServer project:
- XML file to set all security and configuration settings;
- W3C Format Access logging to write connection data to disk;
- Enhanced Disk Eventlog debugging (debugging Server errors or .NET handling errors);
- Built into Windows Service;
- Basic Windows Eventlog logging for critical server failures;
- Specify the Port, Virtual Directory and Physical Path of the .NET web application in the XML file;
- Direct File Handling and content-type handling for any configured file type (e.g.: SWF, JPEG etc.);
- Security for maximum length of URI (Request);
- Security for accepting either local or remote IP addresses;
- Configure only specific IP addresses that are able to connect and process content;
- Disable/Enable directory browsing;
- Custom Version/Server Header configuration;
- Security for handling only specific Request types such as GET, HEAD and POST;
- Disable certain file extensions from being handled by the server;
- Define multiple default document names for the server.
Although I don�t believe that changes made to the source ensure the xNetServer is secure, it is certainly more secure than the basic Cassini project and allows developers to easily change the security and server configuration to their specific requirements. I don�t recommend that the project be used to host Web content directly onto the Internet. (Use at your own risk).
Background
Cassini provides the basic hosting capabilities to run an ASP.NET application server on. Though the server itself has very limited security features, it is widely used on development systems and is included in the Visual Studio 2003/2005 releases as the Web Development server. The application serving capabilities are quite efficient and provide a reasonable platform for serving ASP.NET content.
Information on downloading the original Microsoft Cassini Project source code can be found here.
Configuring the Demo
xNetServer is configured by using the XML file located in the service (installation directory) of the server. The XML file contains a number of settings that when the service is started are read into memory and processed by the server.
The configuration section of the XML file consists of:
<Configuration>
<LoggingDirectory Value="C:\" />
<EnableDebugLogging Value="True" />
<VersionString Value="xNetServer ASP.NET Web Server Version 1.0.1" />
<ServerPort Value="80" />
<VirtualPath Value="/" />
<PhysicalDirectory Value="C:\Inetpub\wwwroot\Chart\" />
<SecurityLocalRequestsOnly Value="False" />
<MaxURILength Value="512" />
<AllowDirectoryListing Value="True" />
</Configuration>
LoggingDirectory
� Specifies the root directory where all Access, Security and Debug logs for the server are written.
EnableDebugLogging
� If set to True
, any errors caused in the processing of a request will be written to the disk event log.
VersionString
� The name the server will use in the Response Header to the client to identify the server type.
ServerPort
� The TCP port the server will listen for requests.
VirtualPath
� The virtual directory if any to be used in the server.
PhysicalDirectory
� The physical root directory of the web server.
SecurityLocalRequestsOnly
� Set to True
if the server is to only accept local security requests, False
if any IP is allowed.
MaxURILength
� Sets the maximum length the URL and querystring can be before a security event is thrown and the request is denied.
AllowDirectoryListing
� If set to False
, the server will not allow directory browsing.
The ContentTypes
section of the XML configuration file allows the server to respond to the requesting client with the correct ContentType Headers.
<ContentTypes>
-->
<ContentType Extension=".jpg" ContentType="image/jpeg" />
<ContentType Extension=".gif" ContentType="image/gif" />
<ContentType Extension=".jpe" ContentType="image/jpeg" />
<ContentType Extension=".jpeg" ContentType="image/jpeg" />
<ContentType Extension=".xml" ContentType="text/xml" />
<ContentType Extension=".gz" ContentType="application/x-gzip" />
<ContentType Extension=".swf" ContentType="application/x-shockwave-flash" />
<ContentType Extension=".ram" ContentType="audio/x-pn-realaudio" />
<ContentType Extension=".crt" ContentType="application/x-x509-ca-cert" />
<ContentType Extension=".tgz" ContentType="application/x-compressed" />
<ContentType Extension=".pdf" ContentType="application/pdf" />
<ContentType Extension=".cer" ContentType="application/x-x509-ca-cert" />
<ContentType Extension=".png" ContentType="image/png" />
<ContentType Extension=".tiff" ContentType="image/tiff" />
<ContentType Extension=".mpg" ContentType="video/mpeg" />
<ContentType Extension=".mpeg" ContentType="video/mpeg" />
-->
</ContentTypes>
More content types can be added by simply replicating lines with their specific content types and extensions. If the content type is not listed in this section, the ASP.NET worker process will handle the request and return Application/Octet content types.
The AccessSecurity
section defines the IP addresses or global settings for clients making requests against the server.
<AccessSecurity>
-->
<GlobalAllowAll Value="True" />
<AllowedIPAddress Value="127.0.0.1" />
<AllowedIPAddress Value="10.1.1.100" />
</AccessSecurity>
If the GlobalAllowAll
setting is set to TRUE
, then all remote clients can connect and communicate with the server. If this is set to False
, then any IP address listed in the AllowedIPAddress
can connect to the server. To include additional IP addresses, just add a new entry in the AccessSecurity
section like: <AllowedIPAddress Value=�x.x.x.x� />
.
The FileAccessSecurity
section allows the ability to define the extensions that are authorized to be processed on the server. If the file extension requested is not listed in the configuration file, then a Security event is fired and the request is denied.
<FileAccessSecurity>
-->
<AllowedFileExtension Type=".asmx" />
<AllowedFileExtension Type=".aspx" />
<AllowedFileExtension Type=".ascx" />
<AllowedFileExtension Type=".jpg" />
<AllowedFileExtension Type=".gif" />
<AllowedFileExtension Type=".htm" />
<AllowedFileExtension Type=".html" />
<AllowedFileExtension Type=".asax" />
<AllowedFileExtension Type=".css" />
<AllowedFileExtension Type=".js" />
</FileAccessSecurity>
The VERBSecurity
configuration section allows the server to be configured to support only specific VERB requests from remote clients.
<VERBSecurity>
-->
<AllowedVERB Type="GET" />
<AllowedVERB Type="HEAD" />
<AllowedVERB Type="POST" />
</VERBSecurity>
New VERBs that are supported can be added by simply creating an additional line in the configuration file with the correct VERB.
Default documents can be specified on a per server basis. These are created by simply adding additional document names to the relevant DefaultDocuments
section of the XML configuration file.
<DefaultDocuments>
-->
<DefaultFile Name="default.aspx" />
<DefaultFile Name="index.aspx" />
<DefaultFile Name="default.htm" />
</DefaultDocuments>
Once you have made the relevant changes to your XML configuration file and installed the service (and configured the xHosting.DLL into the GAC) then you can simply start your server and start processing your HTTP requests. If you wish to customize the hosting DLL or server, please read the �Using the Code� section.
Using the code
Basically, the project is made up of two distinct parts. The Server (xNetServer) and Library (xhosting.dll). The hosting library is directly a VB.NET conversion from the C# Microsoft Cassini project, though a number of security enhancements have been made to ensure the server is much more secure and supports the serving of contents more efficiently.
The xNetServer service is the Windows VB.NET service and is called through the following code:
Dim fullExecPath As String = _
System.Reflection.Assembly.GetExecutingAssembly.Location.ToString
Dim lastSlh As Integer = fullExecPath.LastIndexOf("\"c) + 1
Dim startLocation As String = Left(fullExecPath, lastSlh)
'Lets check and ensure the xNetServer.XML File
'is in the local directory and we can read it.
xConfigXMLReader.ReadXMLAttributesfromXMLFile(startLocation _
& "xNetServerConfig.XML")
If xConfigXMLReader._XMLPhysicalPath.Length <= 1 Then
'We didn't get values out of the XML Document
'(we can't find the document) fail the server.
WindowsEventLog.CreateError("Failed to read the XML Configuration" & _
" File correctly for Startup. Check the directory," & _
" file location and permissions and try again.", 1)
SrvController.Stop()
End If
'Start the Service.
Try
Dim myServer As New Server(xConfigXMLReader._XMLServerPort, _
xConfigXMLReader._XMLVirtualPath, _
xConfigXMLReader._XMLPhysicalPath, _
xConfigXMLReader._XMLLoggingDirectory, _
startLocation)
myServer.Start()
Catch ex As Exception
WindowsEventLog.CreateError("Unable to successfully start" & _
" and bind to the Server Port. Check your configuration file" & _
" and ensure nothing is currently listening on the configured port." _
& vbCrLf & ex.Message & vbCrLf & _
ex.Source & vbCrLf & ex.StackTrace, 2)
SrvController.Stop()
End Try
To start the xHosting.DLL service, you will need to call the Server
object and pass the basic configuration settings (server port, virtual path, physical path, logging directory and the start location of the service). Once the service loads, you will be able to connect via your web browser to the service.
To start the service, the xHosting.DLL class library needs to be installed into the machines GAC cache. To do this, use the gacutil.exe program like so: gacutil.exe /I xhosting.dll. See the reason for the GAC cache on the official Microsoft .NET forums site.
The code inside the class library is very similar to the Cassini project, I have provided a brief diagram below that explains how the Web Hosting library functions and calls content.
(Server)
This provides the initial startup and operation commands for the server.
(Host)
Controls the AppDomain for hosting ASP.NET web content. Also provides some of the header content parsing etc.
(Connection)
Provides the TCP connection handling for each connection made to the server.
(Messages)
Creates and stores any messages that are returned back to the requesting client, such as directly listing content or error messages.
(Request)
This is the primary event handling module and handles each request being made against the server. Includes parsing of data, reading and responding to headers and also checking the security of the request.
(DebugWriter)
Simple class that provides disk event logging for debug events.
(w3Logging)
Allows the server to write request events and security related events to the disk. Each W3log is written with a date-time stamp for the filename and is recorded in W3C logging format.
(xXMLReader)
A basic XML reader class that parses the XML configuration file upon startup and places the configuration settings into memory.
If you recompile the xHosting.dll, I have included (and already scripted) the xhosting.dll strong name file (.SNK). This file is needed to sign the xHosting.dll file before it can be loaded into the system's GAC Cache. All files in the source should simply unzip into a directory and use Visual Studio 2003 to load the project file.
Overall, the purpose of this project was to convert the Microsoft open shared library Cassini C# project into VB.NET so I could learn about the way it was built. At the same time, I identified that there was no security in the Cassini project, so I decided to add a number of features for logging, debugging and security.
I welcome any sensible criticism or recommendations to make this code better. If you are planning on using this code, ensure you read the license agreements from Microsoft (EULA) in the ZIP files.
History
Currently version 1.0.1. (However, plans are to increase security and fix all the bugs everyone finds. Also plans are to create a Microsoft .NET Framework 2 version as well.)