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.
510 lines
11 KiB
510 lines
11 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// File: ktsock.cxx
|
|
//
|
|
// Contents: Kerberos Tunneller, socket operations
|
|
//
|
|
// History: 28-Jun-2001 t-ryanj Created
|
|
//
|
|
//------------------------------------------------------------------------
|
|
#include <Winsock2.h>
|
|
#include <Mswsock.h>
|
|
#include "ktdebug.h"
|
|
#include "ktsock.h"
|
|
#include "ktcontrol.h"
|
|
#include "ktmem.h"
|
|
#include "ktkerb.h"
|
|
|
|
#define KDC_SERVICE_NAME "kerberos"
|
|
#define KDC_FALLBACK_PORT 88
|
|
|
|
SHORT KtListenPort;
|
|
SOCKET KtListenSocket = INVALID_SOCKET;
|
|
BOOL KtWSAStarted = FALSE;
|
|
|
|
VOID
|
|
KtInitListenPort(
|
|
VOID
|
|
)
|
|
{
|
|
PSERVENT krb5;
|
|
|
|
//
|
|
// Ask winsock what port kerberos works on. This should be defined in
|
|
// %systemroot%\system32\drivers\etc\services
|
|
// Note that winsock manages the servent struct, we don't need to free it.
|
|
//
|
|
|
|
if( krb5 = getservbyname( KDC_SERVICE_NAME, NULL ) )
|
|
{
|
|
KtListenPort = krb5->s_port;
|
|
}
|
|
else
|
|
{
|
|
DebugLog( DEB_WARN, "%s(%d): Could not determine kerberos port; falling back to port %d.\n", __FILE__, __LINE__, KDC_FALLBACK_PORT );
|
|
KtListenPort = htons( KDC_FALLBACK_PORT );
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtInitWinsock
|
|
//
|
|
// Synopsis: Starts winsock
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtInitWinsock(
|
|
VOID
|
|
)
|
|
{
|
|
WORD wWinsockVersionRequested = MAKEWORD( 2,2 );
|
|
WSADATA wsaData;
|
|
DWORD WSAStartError;
|
|
|
|
DsysAssert(!KtWSAStarted);
|
|
WSAStartError = WSAStartup( wWinsockVersionRequested, &wsaData );
|
|
if( WSAStartError == SOCKET_ERROR )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error starting winsock: 0x%x.\n", __FILE__, __LINE__, WSAGetLastError() );
|
|
SetLastError(WSAGetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
KtInitListenPort();
|
|
|
|
KtWSAStarted = TRUE;
|
|
|
|
Cleanup:
|
|
return KtWSAStarted;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtCleanupWinsock
|
|
//
|
|
// Synopsis: Call WSACleanup, but only if winsock has been started.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: If WSA isn't started, quietly do nothing.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtCleanupWinsock(
|
|
VOID
|
|
)
|
|
{
|
|
if( KtWSAStarted )
|
|
{
|
|
WSACleanup();
|
|
KtWSAStarted = FALSE;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtStartListening
|
|
//
|
|
// Synopsis: Starts listening for connections
|
|
//
|
|
// Effects: Initializes KtListenSocket.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value, if FALSE GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtStartListening(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD LastError;
|
|
BOOL IocpSuccess;
|
|
sockaddr_in sa;
|
|
|
|
//
|
|
// Initialize the address want to bind to.
|
|
//
|
|
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = KtListenPort;
|
|
sa.sin_addr.S_un.S_addr = 0x0100007f; /* 127.0.0.1 :: loopback */
|
|
|
|
//
|
|
// Create the socket
|
|
//
|
|
|
|
DsysAssert(KtListenSocket==INVALID_SOCKET);
|
|
KtListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
|
if( KtListenSocket == INVALID_SOCKET )
|
|
{
|
|
SetLastError(WSAGetLastError());
|
|
DebugLog( DEB_ERROR, "%s(%d): Error creating listen socket: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Bind it to the interface
|
|
//
|
|
|
|
LastError = bind( KtListenSocket, (sockaddr*)&sa, sizeof(sa) );
|
|
if( LastError == SOCKET_ERROR )
|
|
{
|
|
SetLastError(WSAGetLastError());
|
|
DebugLog( DEB_ERROR, "%s(%d): Error binding listen socket: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Start Listening
|
|
//
|
|
|
|
LastError = listen( KtListenSocket, SOMAXCONN );
|
|
if( LastError == SOCKET_ERROR )
|
|
{
|
|
SetLastError(WSAGetLastError());
|
|
DebugLog( DEB_ERROR, "%s(%d): Error listening on listen socket: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Associate the listen socket with the completion i/o port
|
|
//
|
|
|
|
IocpSuccess = (KtIocp == CreateIoCompletionPort( (HANDLE)KtListenSocket,
|
|
KtIocp,
|
|
KTCK_CHECK_CONTEXT,
|
|
0 ));
|
|
if( !IocpSuccess )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Could not associate listen socket with iocp: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Issue an accept
|
|
//
|
|
|
|
if( !KtSockAccept() )
|
|
goto Error;
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
Error:
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtStopListening
|
|
//
|
|
// Synopsis: Stops listening for new connections.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: If the listen socket isn't valid, quietly do nothing.
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
VOID
|
|
KtStopListening(
|
|
VOID
|
|
)
|
|
{
|
|
if( KtListenSocket != INVALID_SOCKET )
|
|
{
|
|
closesocket(KtListenSocket);
|
|
KtListenSocket = INVALID_SOCKET;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtSockAccept
|
|
//
|
|
// Synopsis: Issues a new AcceptEx on KtListenSocket.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtSockAccept(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
BOOL AcceptSuccess;
|
|
DWORD LastError;
|
|
PKTCONTEXT pContext = NULL;
|
|
SOCKET sock = INVALID_SOCKET;
|
|
|
|
//
|
|
// Create a socket.
|
|
//
|
|
|
|
sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
|
if( sock == INVALID_SOCKET )
|
|
{
|
|
SetLastError(WSAGetLastError());
|
|
DebugLog( DEB_ERROR, "%s(%d): Error creating client socket: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Create a new context
|
|
//
|
|
|
|
pContext = KtAcquireContext( sock, KTCONTEXT_BUFFER_LENGTH );
|
|
if( !pContext )
|
|
goto Error;
|
|
|
|
sock = INVALID_SOCKET;
|
|
|
|
//
|
|
// Mark for a connect operation, and use AcceptEx to issue an overlapped
|
|
// accept on the socket.
|
|
//
|
|
|
|
pContext->Status = KT_SOCK_CONNECT;
|
|
|
|
AcceptSuccess = AcceptEx( KtListenSocket,
|
|
pContext->sock,
|
|
pContext->emptybuf->buffer, /* docbug: contrary to msdn, NULL here doesn't work */
|
|
0,
|
|
sizeof( sockaddr_in ) + 16, /* these addrs require 16 bytes */
|
|
sizeof( sockaddr_in ) + 16, /* of padding - see AcceptEx msdn */
|
|
NULL,
|
|
&(pContext->ol) );
|
|
if( !AcceptSuccess && (WSAGetLastError() != ERROR_IO_PENDING) )
|
|
{
|
|
SetLastError(WSAGetLastError());
|
|
DebugLog( DEB_ERROR, "%s(%d): Error issuing accept: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
Error:
|
|
if( sock != INVALID_SOCKET )
|
|
closesocket(sock);
|
|
|
|
if( pContext )
|
|
KtReleaseContext( pContext );
|
|
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtSockCompleteAccept
|
|
//
|
|
// Synopsis: Associates a new connection with the iocp.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pContext - Contains the info on the connection.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtSockCompleteAccept(
|
|
IN PKTCONTEXT pContext
|
|
)
|
|
{
|
|
DWORD LastError;
|
|
BOOL IocpSuccess;
|
|
|
|
//
|
|
// Associate the newly accepted socket with the completion port.
|
|
//
|
|
|
|
IocpSuccess = (KtIocp == CreateIoCompletionPort( (HANDLE)pContext->sock,
|
|
KtIocp,
|
|
KTCK_CHECK_CONTEXT,
|
|
0 ) );
|
|
if( !IocpSuccess )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error associating client socket with completion port: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Cleanup;
|
|
}
|
|
|
|
DebugLog( DEB_TRACE, "%s(%d): Accepted new connection.\n", __FILE__, __LINE__ );
|
|
|
|
Cleanup:
|
|
return IocpSuccess;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtSockRead
|
|
//
|
|
// Synopsis: Issues a Read.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pContext -
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtSockRead(
|
|
IN PKTCONTEXT pContext
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
BOOL ReadFileSuccess;
|
|
|
|
//
|
|
// Zero the overlapped structure.
|
|
//
|
|
|
|
ZeroMemory( &(pContext->ol), sizeof(OVERLAPPED) );
|
|
|
|
//
|
|
// Mark for a read and use ReadFile to issue an overlapped read.
|
|
//
|
|
|
|
pContext->Status = KT_SOCK_READ;
|
|
|
|
ReadFileSuccess = ReadFile( (HANDLE)pContext->sock,
|
|
pContext->emptybuf->buffer,
|
|
pContext->emptybuf->buflen,
|
|
NULL,
|
|
&(pContext->ol) );
|
|
if( !ReadFileSuccess )
|
|
{
|
|
if( GetLastError() != ERROR_IO_PENDING )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error issuing read: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
Error:
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KtSockWrite
|
|
//
|
|
// Synopsis: Issues a write.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: pContext -
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: Success value. If FALSE, GetLastError() for details.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
KtSockWrite(
|
|
IN PKTCONTEXT pContext
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
BOOL WriteFileSuccess;
|
|
ULONG cbNeeded;
|
|
|
|
//
|
|
// Zero the overlapped
|
|
//
|
|
|
|
ZeroMemory( &(pContext->ol), sizeof(OVERLAPPED) );
|
|
|
|
//
|
|
// Mark for Write and issue overlapped Write with WriteFile
|
|
//
|
|
|
|
pContext->Status = KT_SOCK_WRITE;
|
|
|
|
WriteFileSuccess = WriteFile( (HANDLE)pContext->sock,
|
|
pContext->buffers->buffer,
|
|
pContext->buffers->buflen,
|
|
NULL, /* bytes written - not used in async */
|
|
&(pContext->ol) );
|
|
if( !WriteFileSuccess )
|
|
{
|
|
if( GetLastError() != ERROR_IO_PENDING )
|
|
{
|
|
DebugLog( DEB_ERROR, "%s(%d): Error issuing write: 0x%x.\n", __FILE__, __LINE__, GetLastError() );
|
|
goto Error;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
Error:
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|