//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation // // File: ktsock.cxx // // Contents: Kerberos Tunneller, socket operations // // History: 28-Jun-2001 t-ryanj Created // //------------------------------------------------------------------------ #include #include #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; }