mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
787 lines
20 KiB
787 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Miniwebd.c
|
|
|
|
Abstract:
|
|
|
|
Web server test app.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 8-August-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define FD_SETSIZE 1000
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
|
|
|
|
//
|
|
// richardw: You must define this, and include security.h (or, optionally,
|
|
// sspi.h and issperr.h, for the outside world).
|
|
//
|
|
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
#include <spseal.h>
|
|
|
|
//
|
|
// richardw: package specific definitions: name of package, credential
|
|
// structures used.
|
|
//
|
|
#include <sslsp.h>
|
|
|
|
#define IO_BUFFER_SIZE 4096
|
|
CHAR IoBuffer[IO_BUFFER_SIZE];
|
|
|
|
WSADATA WsaData;
|
|
|
|
BOOL
|
|
ParseRequest (
|
|
IN PCHAR InputBuffer,
|
|
IN INT InputBufferLength,
|
|
OUT PCHAR ObjectName
|
|
);
|
|
|
|
SECURITY_STATUS
|
|
LoadKeys(
|
|
IN PSTR pszPrivateKeyFile,
|
|
IN PSTR pszCertificateFile,
|
|
IN PSTR pszPassword,
|
|
OUT PCredHandle phCreds);
|
|
|
|
VOID
|
|
WebServer (
|
|
PCredHandle phCreds
|
|
);
|
|
|
|
void _CRTAPI1
|
|
main (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
SOCKET s;
|
|
SOCKADDR_IN address;
|
|
INT err;
|
|
SECURITY_STATUS scRet;
|
|
CredHandle hCreds; // richardw: Credential handle. 1 per cert/key
|
|
|
|
if (argc < 4)
|
|
{
|
|
printf("%s: usage\n%s PrivateKeyFile.DER CertificateFile.DER password\n", argv[0], argv[0]);
|
|
exit( 1 );
|
|
}
|
|
|
|
err = WSAStartup( 0x0101, &WsaData );
|
|
if ( err == SOCKET_ERROR ) {
|
|
return;
|
|
}
|
|
|
|
scRet = LoadKeys( argv[1], argv[2], argv[3], &hCreds );
|
|
|
|
if ( FAILED( scRet ) )
|
|
{
|
|
printf("Could not load keys, error %#x\n", scRet );
|
|
exit( 1 );
|
|
}
|
|
|
|
WebServer( &hCreds );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LoadKeys
|
|
//
|
|
// Synopsis: This loads the data contained in two files, a private key
|
|
// file, which contains the key, and a certificate file,
|
|
// which contains the certificate of the public portion of the key.
|
|
// These are loaded, then turned into a credential handle
|
|
//
|
|
// Arguments: [pszPrivateKeyFile] -- ansi file name
|
|
// [pszCertificateFile] -- ansi file name
|
|
// [phCreds] -- (out) handle to creds
|
|
//
|
|
// History: 9-27-95 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
SECURITY_STATUS
|
|
LoadKeys(
|
|
IN PSTR pszPrivateKeyFile,
|
|
IN PSTR pszCertificateFile,
|
|
IN PSTR pszPassword,
|
|
OUT PCredHandle phCreds)
|
|
{
|
|
HANDLE hFile;
|
|
SSL_CREDENTIAL_CERTIFICATE creds;
|
|
DWORD cbRead;
|
|
SECURITY_STATUS scRet;
|
|
TimeStamp tsExpiry;
|
|
|
|
//
|
|
// Fetch data from files:
|
|
//
|
|
|
|
hFile = CreateFileA( pszPrivateKeyFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
creds.cbPrivateKey = GetFileSize( hFile, NULL );
|
|
|
|
if (creds.cbPrivateKey == (DWORD) -1 )
|
|
{
|
|
CloseHandle( hFile );
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
creds.pPrivateKey = LocalAlloc( LMEM_FIXED, creds.cbPrivateKey );
|
|
|
|
if ( !creds.pPrivateKey )
|
|
{
|
|
CloseHandle( hFile );
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
if (! ReadFile( hFile,
|
|
creds.pPrivateKey,
|
|
creds.cbPrivateKey,
|
|
&cbRead,
|
|
NULL ) )
|
|
{
|
|
CloseHandle( hFile );
|
|
|
|
LocalFree( creds.pPrivateKey );
|
|
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
|
|
hFile = CreateFileA( pszCertificateFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
LocalFree( creds.pPrivateKey );
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
creds.cbCertificate = GetFileSize( hFile, NULL );
|
|
|
|
if (creds.cbCertificate == (DWORD) -1 )
|
|
{
|
|
CloseHandle( hFile );
|
|
|
|
LocalFree( creds.pPrivateKey );
|
|
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
creds.pCertificate = LocalAlloc( LMEM_FIXED, creds.cbCertificate );
|
|
|
|
if ( !creds.pCertificate )
|
|
{
|
|
CloseHandle( hFile );
|
|
|
|
LocalFree( creds.pPrivateKey );
|
|
|
|
return( SEC_E_INSUFFICIENT_MEMORY );
|
|
}
|
|
|
|
if (! ReadFile( hFile,
|
|
creds.pCertificate,
|
|
creds.cbCertificate,
|
|
&cbRead,
|
|
NULL ) )
|
|
{
|
|
CloseHandle( hFile );
|
|
|
|
LocalFree( creds.pPrivateKey );
|
|
|
|
LocalFree( creds.pCertificate );
|
|
|
|
return( SEC_E_NO_CREDENTIALS );
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
|
|
//
|
|
// Whew! Now that we have safely loaded the keys from disk, get a cred
|
|
// handle based on the certificate/prv key combo
|
|
//
|
|
|
|
creds.pszPassword = pszPassword;
|
|
|
|
scRet = AcquireCredentialsHandleW( NULL, // My name (ignored)
|
|
SSLSP_NAME_W, // Package
|
|
SECPKG_CRED_INBOUND,// Use
|
|
NULL, // Logon Id (ign.)
|
|
&creds, // auth data
|
|
NULL, // dce-stuff
|
|
NULL, // dce-stuff
|
|
phCreds, // Handle
|
|
&tsExpiry );
|
|
|
|
//
|
|
// Zero out and free the key data memory, on success or fail
|
|
//
|
|
|
|
ZeroMemory( creds.pPrivateKey, creds.cbPrivateKey );
|
|
ZeroMemory( creds.pCertificate, creds.cbCertificate );
|
|
|
|
LocalFree( creds.pPrivateKey );
|
|
LocalFree( creds.pCertificate );
|
|
|
|
//
|
|
// Tell the caller about it.
|
|
//
|
|
|
|
return( scRet );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
WebServer (
|
|
PCredHandle phCreds // richardw: handle to use
|
|
)
|
|
{
|
|
SOCKET listenSocket;
|
|
SOCKET acceptSocket;
|
|
SOCKADDR_IN address;
|
|
INT err;
|
|
BOOL success;
|
|
SOCKADDR_IN remoteAddress;
|
|
INT remoteSockaddrLength;
|
|
INT i;
|
|
PCHAR s;
|
|
CHAR objectName[256];
|
|
DWORD currentDirectoryLength;
|
|
HANDLE objectHandle;
|
|
BY_HANDLE_FILE_INFORMATION fileInfo;
|
|
INT bytesSent;
|
|
DWORD bytesRead;
|
|
|
|
|
|
//
|
|
// richardw: new variables
|
|
//
|
|
|
|
BOOL AuthInProgress = FALSE;
|
|
BOOL AuthFailed = FALSE;
|
|
CtxtHandle hContext;
|
|
SecBufferDesc Message;
|
|
SecBuffer Buffers[4];
|
|
SecBufferDesc MessageOut;
|
|
SecBuffer OutBuffers[4];
|
|
TimeStamp tsExpiry;
|
|
DWORD ContextAttributes;
|
|
SECURITY_STATUS scRet;
|
|
SecPkgContext_Sizes Sizes;
|
|
DWORD TotalSize;
|
|
|
|
//
|
|
// richardw: initialize security buffer structs
|
|
//
|
|
|
|
Message.ulVersion = SECBUFFER_VERSION;
|
|
Message.cBuffers = 4;
|
|
Message.pBuffers = Buffers;
|
|
|
|
Buffers[0].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
|
Buffers[3].BufferType = SECBUFFER_EMPTY;
|
|
|
|
MessageOut.ulVersion = SECBUFFER_VERSION;
|
|
MessageOut.cBuffers = 4;
|
|
MessageOut.pBuffers = OutBuffers;
|
|
|
|
OutBuffers[0].BufferType = SECBUFFER_EMPTY;
|
|
OutBuffers[1].BufferType = SECBUFFER_EMPTY;
|
|
OutBuffers[2].BufferType = SECBUFFER_EMPTY;
|
|
OutBuffers[3].BufferType = SECBUFFER_EMPTY;
|
|
|
|
|
|
|
|
//
|
|
// Figure out our current directory so that we can open files
|
|
// relative to it.
|
|
//
|
|
|
|
currentDirectoryLength = GetCurrentDirectory( 256, objectName );
|
|
if ( currentDirectoryLength == 0 ) {
|
|
printf( "GetCurrentDirectory failed: %ld\n", GetLastError( ) );
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Set up a socket listening on the HTTP port, port 443
|
|
//
|
|
|
|
listenSocket = socket( AF_INET, SOCK_STREAM, 0 );
|
|
if ( listenSocket == INVALID_SOCKET ) {
|
|
printf( "socket() failed for listenSocket: %ld\n", GetLastError( ) );
|
|
exit(1);
|
|
}
|
|
|
|
RtlZeroMemory( &address, sizeof(address) );
|
|
address.sin_family = AF_INET;
|
|
address.sin_port = htons( 443 ); // https port
|
|
|
|
err = bind( listenSocket, (PSOCKADDR)&address, sizeof(address) );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "bind failed: %ld\n", GetLastError( ) );
|
|
exit(1);
|
|
}
|
|
|
|
err = listen( listenSocket, 20 );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "listen failed: %ld\n", GetLastError( ) );
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Loop processing connections.
|
|
//
|
|
|
|
while (TRUE ) {
|
|
|
|
//
|
|
// First accept an incoming connection.
|
|
//
|
|
|
|
remoteSockaddrLength = sizeof(remoteAddress);
|
|
|
|
printf("Waiting for connection\n");
|
|
|
|
acceptSocket = accept( listenSocket, (LPSOCKADDR)&remoteAddress,
|
|
&remoteSockaddrLength );
|
|
if ( acceptSocket == INVALID_SOCKET ) {
|
|
printf( "accept() failed: %ld\n", GetLastError( ) );
|
|
exit(1);
|
|
}
|
|
|
|
do
|
|
{
|
|
err = recv( acceptSocket, IoBuffer, IO_BUFFER_SIZE, 0 );
|
|
|
|
if ( err == SOCKET_ERROR )
|
|
{
|
|
printf(" recv failed: %d\n", GetLastError() );
|
|
closesocket( acceptSocket );
|
|
if ( AuthInProgress )
|
|
{
|
|
DeleteSecurityContext( &hContext );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf(" recv auth data, %d bytes\n", err );
|
|
}
|
|
|
|
if (IoBuffer[0] == 0)
|
|
{
|
|
printf("ERROR PACKET FROM CLIENT\n");
|
|
printf(" %02x %02x %02x\n", IoBuffer[0], IoBuffer[1], IoBuffer[2]);
|
|
AuthFailed = TRUE;
|
|
break;
|
|
}
|
|
|
|
Buffers[0].pvBuffer = IoBuffer;
|
|
Buffers[0].cbBuffer = err;
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
|
|
scRet = AcceptSecurityContext(
|
|
phCreds,
|
|
( AuthInProgress ? &hContext : NULL ),
|
|
&Message,
|
|
ASC_REQ_CONFIDENTIALITY |
|
|
ASC_REQ_ALLOCATE_MEMORY,
|
|
SECURITY_NATIVE_DREP,
|
|
&hContext,
|
|
&MessageOut,
|
|
&ContextAttributes,
|
|
&tsExpiry );
|
|
|
|
if ( SUCCEEDED( scRet ) )
|
|
{
|
|
printf("AcceptSecurityContext returned %x, sending %d bytes back to client\n",
|
|
scRet, OutBuffers[0].cbBuffer);
|
|
|
|
err = send( acceptSocket,
|
|
OutBuffers[0].pvBuffer,
|
|
OutBuffers[0].cbBuffer,
|
|
0 );
|
|
|
|
FreeContextBuffer( OutBuffers[0].pvBuffer );
|
|
|
|
if ( err == SOCKET_ERROR )
|
|
{
|
|
AuthInProgress = FALSE;
|
|
AuthFailed = TRUE;
|
|
}
|
|
|
|
else if ( scRet == SEC_I_CONTINUE_NEEDED )
|
|
{
|
|
AuthInProgress = TRUE;
|
|
AuthFailed = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We completed the auth exchanges!
|
|
//
|
|
|
|
AuthInProgress = FALSE ;
|
|
AuthFailed = FALSE ;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("AcceptSecurityContext returned failure, %#x\n", scRet );
|
|
|
|
if ( AuthInProgress )
|
|
{
|
|
DeleteSecurityContext( &hContext );
|
|
}
|
|
|
|
AuthInProgress = FALSE ;
|
|
AuthFailed = TRUE ;
|
|
}
|
|
|
|
|
|
} while ( AuthInProgress );
|
|
|
|
if ( AuthFailed )
|
|
{
|
|
printf("Auth Failed, closing\n");
|
|
|
|
closesocket( acceptSocket );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf("Auth succeeded, ready for command\n");
|
|
|
|
//
|
|
// Find out how big the header will be:
|
|
//
|
|
|
|
|
|
scRet = QueryContextAttributesW(&hContext,
|
|
SECPKG_ATTR_SIZES,
|
|
&Sizes );
|
|
|
|
|
|
//
|
|
// Receive the HTTP request from the client. Note the
|
|
// assumption that the client will send the request all in one
|
|
// chunk.
|
|
//
|
|
|
|
err = recv( acceptSocket, IoBuffer, IO_BUFFER_SIZE, 0 );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "recv failed: %ld\n", GetLastError( ) );
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
Buffers[0].pvBuffer = IoBuffer;
|
|
Buffers[0].cbBuffer = err;
|
|
Buffers[0].BufferType = SECBUFFER_DATA;
|
|
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
|
|
|
scRet = UnsealMessage( &hContext,
|
|
&Message,
|
|
0,
|
|
NULL );
|
|
|
|
if ( FAILED( scRet ) )
|
|
{
|
|
if ( scRet == SEC_E_INCOMPLETE_MESSAGE )
|
|
{
|
|
//
|
|
// BUGBUG: This is where I say, hey! the message you
|
|
// passed is not as long as was encoded. This means that
|
|
// the server must get the next n bytes (as returned), and
|
|
// call again *WITH THE COMPLETE MESSAGE*
|
|
//
|
|
// This is left as an exercise for the reader.
|
|
//
|
|
|
|
printf("Message is short %d bytes\n", Buffers[1].cbBuffer);
|
|
}
|
|
|
|
//
|
|
// Couldn't decrypt the message
|
|
//
|
|
|
|
printf(" UnsealMessaged returned %#x\n", scRet );
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Technically, walk through the buffer desc, looking for the first
|
|
// buffer labelled SECBUFFER_DATA. Practically, it will be the second
|
|
// buffer section.
|
|
//
|
|
|
|
printf("Message is: '%s'\n", Buffers[1].pvBuffer);
|
|
|
|
|
|
//
|
|
// Parse the request in order to determine the requested object.
|
|
// Note that we only handle the GET verb in this server.
|
|
//
|
|
|
|
success = ParseRequest(
|
|
Buffers[1].pvBuffer,
|
|
Buffers[1].cbBuffer,
|
|
objectName+currentDirectoryLength );
|
|
if ( !success ) {
|
|
printf("Unable to parse message\n");
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Open the requested object.
|
|
//
|
|
|
|
objectHandle = CreateFileA(
|
|
objectName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
if ( objectHandle == INVALID_HANDLE_VALUE ) {
|
|
printf( "CreateFile(%s) failed: %ld\n", objectName, GetLastError( ) );
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Determine the length of the file.
|
|
//
|
|
|
|
success = GetFileInformationByHandle( objectHandle, &fileInfo );
|
|
if ( !success ) {
|
|
printf( "GetFileInformationByHandle failed: %ld\n", GetLastError( ) );
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Build and the HTTP response header.
|
|
//
|
|
|
|
ZeroMemory( IoBuffer, Sizes.cbSecurityTrailer );
|
|
|
|
i = sprintf( IoBuffer + Sizes.cbSecurityTrailer,
|
|
"HTTP/1.0 200 OK\r\nContent-Length: %d\r\n",
|
|
fileInfo.nFileSizeLow );
|
|
|
|
|
|
//
|
|
// Line up the buffers so that the header and content will be
|
|
// all set to go.
|
|
//
|
|
|
|
Buffers[0].pvBuffer = IoBuffer;
|
|
Buffers[0].cbBuffer = Sizes.cbSecurityTrailer;
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
|
|
Buffers[1].pvBuffer = IoBuffer + Sizes.cbSecurityTrailer;
|
|
Buffers[1].cbBuffer = i;
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
|
|
|
|
scRet = SealMessage(&hContext,
|
|
0,
|
|
&Message,
|
|
0);
|
|
|
|
if ( FAILED( scRet ) )
|
|
{
|
|
printf(" SealMessage failed with %#x\n", scRet );
|
|
}
|
|
|
|
|
|
err = send( acceptSocket,
|
|
IoBuffer,
|
|
Buffers[0].cbBuffer + Buffers[1].cbBuffer,
|
|
0 );
|
|
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "send failed: %ld\n", GetLastError( ) );
|
|
closesocket( acceptSocket );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now read and send the file data.
|
|
//
|
|
|
|
for ( bytesSent = 0;
|
|
bytesSent < (INT)fileInfo.nFileSizeLow;
|
|
bytesSent += err ) {
|
|
|
|
success = ReadFile(
|
|
objectHandle,
|
|
IoBuffer + Sizes.cbSecurityTrailer,
|
|
IO_BUFFER_SIZE - Sizes.cbSecurityTrailer,
|
|
&bytesRead,
|
|
NULL
|
|
);
|
|
if ( !success ) {
|
|
printf( "ReadFile failed: %ld\n", GetLastError( ) );
|
|
break;
|
|
}
|
|
|
|
Buffers[0].pvBuffer = IoBuffer;
|
|
Buffers[0].cbBuffer = Sizes.cbSecurityTrailer;
|
|
Buffers[0].BufferType = SECBUFFER_TOKEN;
|
|
|
|
Buffers[1].pvBuffer = IoBuffer + Sizes.cbSecurityTrailer;
|
|
Buffers[1].cbBuffer = bytesRead;
|
|
Buffers[1].BufferType = SECBUFFER_DATA;
|
|
|
|
scRet = SealMessage(&hContext,
|
|
0,
|
|
&Message,
|
|
0);
|
|
|
|
if ( FAILED( scRet ) )
|
|
{
|
|
printf(" SealMessage failed with %#x\n", scRet );
|
|
}
|
|
|
|
err = send( acceptSocket,
|
|
IoBuffer,
|
|
bytesRead + Sizes.cbSecurityTrailer, 0 );
|
|
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "send failed: %ld\n", GetLastError( ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, close the socket and the file and continue accepting
|
|
// incoming connections.
|
|
//
|
|
|
|
CloseHandle( objectHandle );
|
|
closesocket( acceptSocket );
|
|
}
|
|
|
|
} // WebServer
|
|
|
|
|
|
|
|
BOOL
|
|
ParseRequest (
|
|
IN PCHAR InputBuffer,
|
|
IN INT InputBufferLength,
|
|
OUT PCHAR ObjectName
|
|
)
|
|
{
|
|
PCHAR s = InputBuffer;
|
|
DWORD i;
|
|
|
|
while ( (INT)(s - InputBuffer) < InputBufferLength ) {
|
|
|
|
//
|
|
// First determine whether this line starts with the GET
|
|
// verb.
|
|
//
|
|
|
|
if ( (*s == 'g' || *s == 'G') &&
|
|
(*(s+1) == 'e' || *(s+1) == 'E') &&
|
|
(*(s+2) == 't' || *(s+2) == 'T') ) {
|
|
|
|
//
|
|
// It is a GET. Skip over white space.
|
|
//
|
|
|
|
for ( s += 3; *s == ' ' || *s == '\t'; s++ );
|
|
|
|
//
|
|
// Now grab the object name.
|
|
//
|
|
|
|
for ( i = 0; *s != 0xA && *s != 0xD && *s != ' '; s++, i++ ) {
|
|
ObjectName[i] = *s;
|
|
if ( ObjectName[i] == '/' ) {
|
|
ObjectName[i] = '\\';
|
|
}
|
|
}
|
|
|
|
ObjectName[i] = '\0';
|
|
|
|
//
|
|
// We're done parsing.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Skip to the end of the line and continue parsing.
|
|
//
|
|
|
|
while ( *s != 0xA && *s != 0xD ) {
|
|
s++;
|
|
}
|
|
|
|
s++;
|
|
|
|
if ( *s == 0xD || *s == 0xA ) {
|
|
s++;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // ParseRequest
|